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