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