• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
234 namespace v8 {
235 
236 class Isolate;
237 
238 class 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)
type_(type)287       : type_(type), sequence_type_(sequence_type), flags_(flags) {}
288 
289   typedef uint32_t Identifier;
CTypeInfo(Identifier 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)) {}
GetId()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 
GetType()300   constexpr Type GetType() const { return type_; }
GetSequenceType()301   constexpr SequenceType GetSequenceType() const { return sequence_type_; }
GetFlags()302   constexpr Flags GetFlags() const { return flags_; }
303 
IsIntegralType(Type type)304   static constexpr bool IsIntegralType(Type type) {
305     return type == Type::kInt32 || type == Type::kUint32 ||
306            type == Type::kInt64 || type == Type::kUint64;
307   }
308 
IsFloatingPointType(Type type)309   static constexpr bool IsFloatingPointType(Type type) {
310     return type == Type::kFloat32 || type == Type::kFloat64;
311   }
312 
IsPrimitive(Type type)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 
324 struct FastApiTypedArrayBase {
325  public:
326   // Returns the length in number of elements.
lengthFastApiTypedArrayBase327   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 
335 template <typename T>
336 struct FastApiTypedArray : public FastApiTypedArrayBase {
337  public:
getFastApiTypedArray338   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 
getStorageIfAlignedFastApiTypedArray347   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.
370 struct FastApiArrayBufferView {
371   void* data;
372   size_t byte_length;
373 };
374 
375 struct FastApiArrayBuffer {
376   void* data;
377   size_t byte_length;
378 };
379 
380 class 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 
ReturnInfo()390   const CTypeInfo& ReturnInfo() const { return return_info_; }
391 
392   // The argument count, not including the v8::FastApiCallbackOptions
393   // if present.
ArgumentCount()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 
HasOptions()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 
416 struct FastApiCallbackOptions;
417 
418 // Provided for testing.
419 struct AnyCType {
AnyCTypeAnyCType420   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 
442 static_assert(
443     sizeof(AnyCType) == 8,
444     "The AnyCType struct should have size == 64 bits, as this is assumed "
445     "by EffectControlLinearizer.");
446 
447 class V8_EXPORT CFunction {
448  public:
CFunction()449   constexpr CFunction() : address_(nullptr), type_info_(nullptr) {}
450 
ReturnInfo()451   const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); }
452 
ArgumentInfo(unsigned int index)453   const CTypeInfo& ArgumentInfo(unsigned int index) const {
454     return type_info_->ArgumentInfo(index);
455   }
456 
ArgumentCount()457   unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); }
458 
GetAddress()459   const void* GetAddress() const { return address_; }
GetTypeInfo()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.
GetOverloadResolution(const CFunction * other)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>
Make(F * func)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>
Make(R (* func)(Args...),R_Patch (* patching_func)(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  */
541 struct FastApiCallbackOptions {
542   /**
543    * Creates a new instance of FastApiCallbackOptions for testing purpose.  The
544    * returned instance may be filled with mock data.
545    */
CreateForTestingFastApiCallbackOptions546   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 
573 namespace internal {
574 
575 // Helper to count the number of occurances of `T` in `List`
576 template <typename T, typename... List>
577 struct count : std::integral_constant<int, 0> {};
578 template <typename T, typename... Args>
579 struct count<T, T, Args...>
580     : std::integral_constant<std::size_t, 1 + count<T, Args...>::value> {};
581 template <typename T, typename U, typename... Args>
582 struct count<T, U, Args...> : count<T, Args...> {};
583 
584 template <typename RetBuilder, typename... ArgBuilders>
585 class 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 
617 template <typename T>
618 struct 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 
635 template <CTypeInfo::Type type>
636 struct 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 
665 ALL_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR)
666 PRIMITIVE_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 
692 TYPED_ARRAY_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA)
693 
694 #undef TYPED_ARRAY_C_TYPES
695 
696 template <>
697 struct 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 
706 template <>
707 struct 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 
716 template <>
717 struct 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 
733 template <typename T, CTypeInfo::Flags... Flags>
734 class 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 
784 namespace internal {
785 template <typename RetBuilder, typename... ArgBuilders>
786 class 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 
846 class 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
861 template <typename R, typename... Args>
862 CFunction CFunction::ArgUnwrap<R (*)(Args...)>::Make(R (*func)(Args...)) {
863   return internal::CFunctionBuilder().Fn(func).Build();
864 }
865 
866 using CFunctionBuilder = internal::CFunctionBuilder;
867 
868 static constexpr CTypeInfo kTypeInfoInt32 = CTypeInfo(CTypeInfo::Type::kInt32);
869 static 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  */
884 template <const CTypeInfo* type_info, typename T>
885 V8_DEPRECATED(
886     "Use TryToCopyAndConvertArrayToCppBuffer<CTypeInfo::Identifier, T>()")
887 bool V8_EXPORT V8_WARN_UNUSED_RESULT
888     TryCopyAndConvertArrayToCppBuffer(Local<Array> src, T* dst,
889                                       uint32_t max_length);
890 
891 template <>
892 V8_DEPRECATED(
893     "Use TryToCopyAndConvertArrayToCppBuffer<CTypeInfo::Identifier, T>()")
894 inline 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 
900 template <>
901 V8_DEPRECATED(
902     "Use TryToCopyAndConvertArrayToCppBuffer<CTypeInfo::Identifier, T>()")
903 inline bool V8_WARN_UNUSED_RESULT
904     TryCopyAndConvertArrayToCppBuffer<&kTypeInfoFloat64, double>(
905         Local<Array> src, double* dst, uint32_t max_length) {
906   return false;
907 }
908 
909 template <CTypeInfo::Identifier type_info_id, typename T>
910 bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer(
911     Local<Array> src, T* dst, uint32_t max_length);
912 
913 template <>
914 bool V8_EXPORT V8_WARN_UNUSED_RESULT
915 TryToCopyAndConvertArrayToCppBuffer<CTypeInfoBuilder<int32_t>::Build().GetId(),
916                                     int32_t>(Local<Array> src, int32_t* dst,
917                                              uint32_t max_length);
918 
919 template <>
920 bool V8_EXPORT V8_WARN_UNUSED_RESULT
921 TryToCopyAndConvertArrayToCppBuffer<CTypeInfoBuilder<uint32_t>::Build().GetId(),
922                                     uint32_t>(Local<Array> src, uint32_t* dst,
923                                               uint32_t max_length);
924 
925 template <>
926 bool V8_EXPORT V8_WARN_UNUSED_RESULT
927 TryToCopyAndConvertArrayToCppBuffer<CTypeInfoBuilder<float>::Build().GetId(),
928                                     float>(Local<Array> src, float* dst,
929                                            uint32_t max_length);
930 
931 template <>
932 bool V8_EXPORT V8_WARN_UNUSED_RESULT
933 TryToCopyAndConvertArrayToCppBuffer<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