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