123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939 |
- // Copyright 2020 the V8 project authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- /**
- * This file provides additional API on top of the default one for making
- * API calls, which come from embedder C++ functions. The functions are being
- * called directly from optimized code, doing all the necessary typechecks
- * in the compiler itself, instead of on the embedder side. Hence the "fast"
- * in the name. Example usage might look like:
- *
- * \code
- * void FastMethod(int param, bool another_param);
- *
- * v8::FunctionTemplate::New(isolate, SlowCallback, data,
- * signature, length, constructor_behavior
- * side_effect_type,
- * &v8::CFunction::Make(FastMethod));
- * \endcode
- *
- * By design, fast calls are limited by the following requirements, which
- * the embedder should enforce themselves:
- * - they should not allocate on the JS heap;
- * - they should not trigger JS execution.
- * To enforce them, the embedder could use the existing
- * v8::Isolate::DisallowJavascriptExecutionScope and a utility similar to
- * Blink's NoAllocationScope:
- * https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/platform/heap/thread_state_scopes.h;l=16
- *
- * Due to these limitations, it's not directly possible to report errors by
- * throwing a JS exception or to otherwise do an allocation. There is an
- * alternative way of creating fast calls that supports falling back to the
- * slow call and then performing the necessary allocation. When one creates
- * the fast method by using CFunction::MakeWithFallbackSupport instead of
- * CFunction::Make, the fast callback gets as last parameter an output variable,
- * through which it can request falling back to the slow call. So one might
- * declare their method like:
- *
- * \code
- * void FastMethodWithFallback(int param, FastApiCallbackOptions& options);
- * \endcode
- *
- * If the callback wants to signal an error condition or to perform an
- * allocation, it must set options.fallback to true and do an early return from
- * the fast method. Then V8 checks the value of options.fallback and if it's
- * true, falls back to executing the SlowCallback, which is capable of reporting
- * the error (either by throwing a JS exception or logging to the console) or
- * doing the allocation. It's the embedder's responsibility to ensure that the
- * fast callback is idempotent up to the point where error and fallback
- * conditions are checked, because otherwise executing the slow callback might
- * produce visible side-effects twice.
- *
- * An example for custom embedder type support might employ a way to wrap/
- * unwrap various C++ types in JSObject instances, e.g:
- *
- * \code
- *
- * // Helper method with a check for field count.
- * template <typename T, int offset>
- * inline T* GetInternalField(v8::Local<v8::Object> wrapper) {
- * assert(offset < wrapper->InternalFieldCount());
- * return reinterpret_cast<T*>(
- * wrapper->GetAlignedPointerFromInternalField(offset));
- * }
- *
- * class CustomEmbedderType {
- * public:
- * // Returns the raw C object from a wrapper JS object.
- * static CustomEmbedderType* Unwrap(v8::Local<v8::Object> wrapper) {
- * return GetInternalField<CustomEmbedderType,
- * kV8EmbedderWrapperObjectIndex>(wrapper);
- * }
- * static void FastMethod(v8::Local<v8::Object> receiver_obj, int param) {
- * CustomEmbedderType* receiver = static_cast<CustomEmbedderType*>(
- * receiver_obj->GetAlignedPointerFromInternalField(
- * kV8EmbedderWrapperObjectIndex));
- *
- * // Type checks are already done by the optimized code.
- * // Then call some performance-critical method like:
- * // receiver->Method(param);
- * }
- *
- * static void SlowMethod(
- * const v8::FunctionCallbackInfo<v8::Value>& info) {
- * v8::Local<v8::Object> instance =
- * v8::Local<v8::Object>::Cast(info.Holder());
- * CustomEmbedderType* receiver = Unwrap(instance);
- * // TODO: Do type checks and extract {param}.
- * receiver->Method(param);
- * }
- * };
- *
- * // TODO(mslekova): Clean-up these constants
- * // The constants kV8EmbedderWrapperTypeIndex and
- * // kV8EmbedderWrapperObjectIndex describe the offsets for the type info
- * // struct and the native object, when expressed as internal field indices
- * // within a JSObject. The existance of this helper function assumes that
- * // all embedder objects have their JSObject-side type info at the same
- * // offset, but this is not a limitation of the API itself. For a detailed
- * // use case, see the third example.
- * static constexpr int kV8EmbedderWrapperTypeIndex = 0;
- * static constexpr int kV8EmbedderWrapperObjectIndex = 1;
- *
- * // The following setup function can be templatized based on
- * // the {embedder_object} argument.
- * void SetupCustomEmbedderObject(v8::Isolate* isolate,
- * v8::Local<v8::Context> context,
- * CustomEmbedderType* embedder_object) {
- * isolate->set_embedder_wrapper_type_index(
- * kV8EmbedderWrapperTypeIndex);
- * isolate->set_embedder_wrapper_object_index(
- * kV8EmbedderWrapperObjectIndex);
- *
- * v8::CFunction c_func =
- * MakeV8CFunction(CustomEmbedderType::FastMethod);
- *
- * Local<v8::FunctionTemplate> method_template =
- * v8::FunctionTemplate::New(
- * isolate, CustomEmbedderType::SlowMethod, v8::Local<v8::Value>(),
- * v8::Local<v8::Signature>(), 1, v8::ConstructorBehavior::kAllow,
- * v8::SideEffectType::kHasSideEffect, &c_func);
- *
- * v8::Local<v8::ObjectTemplate> object_template =
- * v8::ObjectTemplate::New(isolate);
- * object_template->SetInternalFieldCount(
- * kV8EmbedderWrapperObjectIndex + 1);
- * object_template->Set(isolate, "method", method_template);
- *
- * // Instantiate the wrapper JS object.
- * v8::Local<v8::Object> object =
- * object_template->NewInstance(context).ToLocalChecked();
- * object->SetAlignedPointerInInternalField(
- * kV8EmbedderWrapperObjectIndex,
- * reinterpret_cast<void*>(embedder_object));
- *
- * // TODO: Expose {object} where it's necessary.
- * }
- * \endcode
- *
- * For instance if {object} is exposed via a global "obj" variable,
- * one could write in JS:
- * function hot_func() {
- * obj.method(42);
- * }
- * and once {hot_func} gets optimized, CustomEmbedderType::FastMethod
- * will be called instead of the slow version, with the following arguments:
- * receiver := the {embedder_object} from above
- * param := 42
- *
- * Currently supported return types:
- * - void
- * - bool
- * - int32_t
- * - uint32_t
- * - float32_t
- * - float64_t
- * Currently supported argument types:
- * - pointer to an embedder type
- * - JavaScript array of primitive types
- * - bool
- * - int32_t
- * - uint32_t
- * - int64_t
- * - uint64_t
- * - float32_t
- * - float64_t
- *
- * The 64-bit integer types currently have the IDL (unsigned) long long
- * semantics: https://heycam.github.io/webidl/#abstract-opdef-converttoint
- * In the future we'll extend the API to also provide conversions from/to
- * BigInt to preserve full precision.
- * The floating point types currently have the IDL (unrestricted) semantics,
- * which is the only one used by WebGL. We plan to add support also for
- * restricted floats/doubles, similarly to the BigInt conversion policies.
- * We also differ from the specific NaN bit pattern that WebIDL prescribes
- * (https://heycam.github.io/webidl/#es-unrestricted-float) in that Blink
- * passes NaN values as-is, i.e. doesn't normalize them.
- *
- * To be supported types:
- * - TypedArrays and ArrayBuffers
- * - arrays of embedder types
- *
- *
- * The API offers a limited support for function overloads:
- *
- * \code
- * void FastMethod_2Args(int param, bool another_param);
- * void FastMethod_3Args(int param, bool another_param, int third_param);
- *
- * v8::CFunction fast_method_2args_c_func =
- * MakeV8CFunction(FastMethod_2Args);
- * v8::CFunction fast_method_3args_c_func =
- * MakeV8CFunction(FastMethod_3Args);
- * const v8::CFunction fast_method_overloads[] = {fast_method_2args_c_func,
- * fast_method_3args_c_func};
- * Local<v8::FunctionTemplate> method_template =
- * v8::FunctionTemplate::NewWithCFunctionOverloads(
- * isolate, SlowCallback, data, signature, length,
- * constructor_behavior, side_effect_type,
- * {fast_method_overloads, 2});
- * \endcode
- *
- * In this example a single FunctionTemplate is associated to multiple C++
- * functions. The overload resolution is currently only based on the number of
- * arguments passed in a call. For example, if this method_template is
- * registered with a wrapper JS object as described above, a call with two
- * arguments:
- * obj.method(42, true);
- * will result in a fast call to FastMethod_2Args, while a call with three or
- * more arguments:
- * obj.method(42, true, 11);
- * will result in a fast call to FastMethod_3Args. Instead a call with less than
- * two arguments, like:
- * obj.method(42);
- * would not result in a fast call but would fall back to executing the
- * associated SlowCallback.
- */
- #ifndef INCLUDE_V8_FAST_API_CALLS_H_
- #define INCLUDE_V8_FAST_API_CALLS_H_
- #include <stddef.h>
- #include <stdint.h>
- #include <tuple>
- #include <type_traits>
- #include "v8-internal.h" // NOLINT(build/include_directory)
- #include "v8-local-handle.h" // NOLINT(build/include_directory)
- #include "v8-typed-array.h" // NOLINT(build/include_directory)
- #include "v8-value.h" // NOLINT(build/include_directory)
- #include "v8config.h" // NOLINT(build/include_directory)
- namespace v8 {
- class Isolate;
- class CTypeInfo {
- public:
- enum class Type : uint8_t {
- kVoid,
- kBool,
- kInt32,
- kUint32,
- kInt64,
- kUint64,
- kFloat32,
- kFloat64,
- kV8Value,
- kApiObject, // This will be deprecated once all users have
- // migrated from v8::ApiObject to v8::Local<v8::Value>.
- kAny, // This is added to enable untyped representation of fast
- // call arguments for test purposes. It can represent any of
- // the other types stored in the same memory as a union (see
- // the AnyCType struct declared below). This allows for
- // uniform passing of arguments w.r.t. their location
- // (in a register or on the stack), independent of their
- // actual type. It's currently used by the arm64 simulator
- // and can be added to the other simulators as well when fast
- // calls having both GP and FP params need to be supported.
- };
- // kCallbackOptionsType is not part of the Type enum
- // because it is only used internally. Use value 255 that is larger
- // than any valid Type enum.
- static constexpr Type kCallbackOptionsType = Type(255);
- enum class SequenceType : uint8_t {
- kScalar,
- kIsSequence, // sequence<T>
- kIsTypedArray, // TypedArray of T or any ArrayBufferView if T
- // is void
- kIsArrayBuffer // ArrayBuffer
- };
- enum class Flags : uint8_t {
- kNone = 0,
- kAllowSharedBit = 1 << 0, // Must be an ArrayBuffer or TypedArray
- kEnforceRangeBit = 1 << 1, // T must be integral
- kClampBit = 1 << 2, // T must be integral
- kIsRestrictedBit = 1 << 3, // T must be float or double
- };
- explicit constexpr CTypeInfo(
- Type type, SequenceType sequence_type = SequenceType::kScalar,
- Flags flags = Flags::kNone)
- : type_(type), sequence_type_(sequence_type), flags_(flags) {}
- typedef uint32_t Identifier;
- explicit constexpr CTypeInfo(Identifier identifier)
- : CTypeInfo(static_cast<Type>(identifier >> 16),
- static_cast<SequenceType>((identifier >> 8) & 255),
- static_cast<Flags>(identifier & 255)) {}
- constexpr Identifier GetId() const {
- return static_cast<uint8_t>(type_) << 16 |
- static_cast<uint8_t>(sequence_type_) << 8 |
- static_cast<uint8_t>(flags_);
- }
- constexpr Type GetType() const { return type_; }
- constexpr SequenceType GetSequenceType() const { return sequence_type_; }
- constexpr Flags GetFlags() const { return flags_; }
- static constexpr bool IsIntegralType(Type type) {
- return type == Type::kInt32 || type == Type::kUint32 ||
- type == Type::kInt64 || type == Type::kUint64;
- }
- static constexpr bool IsFloatingPointType(Type type) {
- return type == Type::kFloat32 || type == Type::kFloat64;
- }
- static constexpr bool IsPrimitive(Type type) {
- return IsIntegralType(type) || IsFloatingPointType(type) ||
- type == Type::kBool;
- }
- private:
- Type type_;
- SequenceType sequence_type_;
- Flags flags_;
- };
- struct FastApiTypedArrayBase {
- public:
- // Returns the length in number of elements.
- size_t V8_EXPORT length() const { return length_; }
- // Checks whether the given index is within the bounds of the collection.
- void V8_EXPORT ValidateIndex(size_t index) const;
- protected:
- size_t length_ = 0;
- };
- template <typename T>
- struct FastApiTypedArray : public FastApiTypedArrayBase {
- public:
- V8_INLINE T get(size_t index) const {
- #ifdef DEBUG
- ValidateIndex(index);
- #endif // DEBUG
- T tmp;
- memcpy(&tmp, reinterpret_cast<T*>(data_) + index, sizeof(T));
- return tmp;
- }
- bool getStorageIfAligned(T** elements) const {
- if (reinterpret_cast<uintptr_t>(data_) % alignof(T) != 0) {
- return false;
- }
- *elements = reinterpret_cast<T*>(data_);
- return true;
- }
- private:
- // This pointer should include the typed array offset applied.
- // It's not guaranteed that it's aligned to sizeof(T), it's only
- // guaranteed that it's 4-byte aligned, so for 8-byte types we need to
- // provide a special implementation for reading from it, which hides
- // the possibly unaligned read in the `get` method.
- void* data_;
- };
- // Any TypedArray. It uses kTypedArrayBit with base type void
- // Overloaded args of ArrayBufferView and TypedArray are not supported
- // (for now) because the generic “any” ArrayBufferView doesn’t have its
- // own instance type. It could be supported if we specify that
- // TypedArray<T> always has precedence over the generic ArrayBufferView,
- // but this complicates overload resolution.
- struct FastApiArrayBufferView {
- void* data;
- size_t byte_length;
- };
- struct FastApiArrayBuffer {
- void* data;
- size_t byte_length;
- };
- class V8_EXPORT CFunctionInfo {
- public:
- // Construct a struct to hold a CFunction's type information.
- // |return_info| describes the function's return type.
- // |arg_info| is an array of |arg_count| CTypeInfos describing the
- // arguments. Only the last argument may be of the special type
- // CTypeInfo::kCallbackOptionsType.
- CFunctionInfo(const CTypeInfo& return_info, unsigned int arg_count,
- const CTypeInfo* arg_info);
- const CTypeInfo& ReturnInfo() const { return return_info_; }
- // The argument count, not including the v8::FastApiCallbackOptions
- // if present.
- unsigned int ArgumentCount() const {
- return HasOptions() ? arg_count_ - 1 : arg_count_;
- }
- // |index| must be less than ArgumentCount().
- // Note: if the last argument passed on construction of CFunctionInfo
- // has type CTypeInfo::kCallbackOptionsType, it is not included in
- // ArgumentCount().
- const CTypeInfo& ArgumentInfo(unsigned int index) const;
- bool HasOptions() const {
- // The options arg is always the last one.
- return arg_count_ > 0 && arg_info_[arg_count_ - 1].GetType() ==
- CTypeInfo::kCallbackOptionsType;
- }
- private:
- const CTypeInfo return_info_;
- const unsigned int arg_count_;
- const CTypeInfo* arg_info_;
- };
- struct FastApiCallbackOptions;
- // Provided for testing.
- struct AnyCType {
- AnyCType() : int64_value(0) {}
- union {
- bool bool_value;
- int32_t int32_value;
- uint32_t uint32_value;
- int64_t int64_value;
- uint64_t uint64_value;
- float float_value;
- double double_value;
- Local<Object> object_value;
- Local<Array> sequence_value;
- const FastApiTypedArray<int32_t>* int32_ta_value;
- const FastApiTypedArray<uint32_t>* uint32_ta_value;
- const FastApiTypedArray<int64_t>* int64_ta_value;
- const FastApiTypedArray<uint64_t>* uint64_ta_value;
- const FastApiTypedArray<float>* float_ta_value;
- const FastApiTypedArray<double>* double_ta_value;
- FastApiCallbackOptions* options_value;
- };
- };
- static_assert(
- sizeof(AnyCType) == 8,
- "The AnyCType struct should have size == 64 bits, as this is assumed "
- "by EffectControlLinearizer.");
- class V8_EXPORT CFunction {
- public:
- constexpr CFunction() : address_(nullptr), type_info_(nullptr) {}
- const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); }
- const CTypeInfo& ArgumentInfo(unsigned int index) const {
- return type_info_->ArgumentInfo(index);
- }
- unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); }
- const void* GetAddress() const { return address_; }
- const CFunctionInfo* GetTypeInfo() const { return type_info_; }
- enum class OverloadResolution { kImpossible, kAtRuntime, kAtCompileTime };
- // Returns whether an overload between this and the given CFunction can
- // be resolved at runtime by the RTTI available for the arguments or at
- // compile time for functions with different number of arguments.
- OverloadResolution GetOverloadResolution(const CFunction* other) {
- // Runtime overload resolution can only deal with functions with the
- // same number of arguments. Functions with different arity are handled
- // by compile time overload resolution though.
- if (ArgumentCount() != other->ArgumentCount()) {
- return OverloadResolution::kAtCompileTime;
- }
- // The functions can only differ by a single argument position.
- int diff_index = -1;
- for (unsigned int i = 0; i < ArgumentCount(); ++i) {
- if (ArgumentInfo(i).GetSequenceType() !=
- other->ArgumentInfo(i).GetSequenceType()) {
- if (diff_index >= 0) {
- return OverloadResolution::kImpossible;
- }
- diff_index = i;
- // We only support overload resolution between sequence types.
- if (ArgumentInfo(i).GetSequenceType() ==
- CTypeInfo::SequenceType::kScalar ||
- other->ArgumentInfo(i).GetSequenceType() ==
- CTypeInfo::SequenceType::kScalar) {
- return OverloadResolution::kImpossible;
- }
- }
- }
- return OverloadResolution::kAtRuntime;
- }
- template <typename F>
- static CFunction Make(F* func) {
- return ArgUnwrap<F*>::Make(func);
- }
- // Provided for testing purposes.
- template <typename R, typename... Args, typename R_Patch,
- typename... Args_Patch>
- static CFunction Make(R (*func)(Args...),
- R_Patch (*patching_func)(Args_Patch...)) {
- CFunction c_func = ArgUnwrap<R (*)(Args...)>::Make(func);
- static_assert(
- sizeof...(Args_Patch) == sizeof...(Args),
- "The patching function must have the same number of arguments.");
- c_func.address_ = reinterpret_cast<void*>(patching_func);
- return c_func;
- }
- CFunction(const void* address, const CFunctionInfo* type_info);
- private:
- const void* address_;
- const CFunctionInfo* type_info_;
- template <typename F>
- class ArgUnwrap {
- static_assert(sizeof(F) != sizeof(F),
- "CFunction must be created from a function pointer.");
- };
- template <typename R, typename... Args>
- class ArgUnwrap<R (*)(Args...)> {
- public:
- static CFunction Make(R (*func)(Args...));
- };
- };
- /**
- * A struct which may be passed to a fast call callback, like so:
- * \code
- * void FastMethodWithOptions(int param, FastApiCallbackOptions& options);
- * \endcode
- */
- struct FastApiCallbackOptions {
- /**
- * Creates a new instance of FastApiCallbackOptions for testing purpose. The
- * returned instance may be filled with mock data.
- */
- static FastApiCallbackOptions CreateForTesting(Isolate* isolate) {
- return {false, {0}};
- }
- /**
- * If the callback wants to signal an error condition or to perform an
- * allocation, it must set options.fallback to true and do an early return
- * from the fast method. Then V8 checks the value of options.fallback and if
- * it's true, falls back to executing the SlowCallback, which is capable of
- * reporting the error (either by throwing a JS exception or logging to the
- * console) or doing the allocation. It's the embedder's responsibility to
- * ensure that the fast callback is idempotent up to the point where error and
- * fallback conditions are checked, because otherwise executing the slow
- * callback might produce visible side-effects twice.
- */
- bool fallback;
- /**
- * The `data` passed to the FunctionTemplate constructor, or `undefined`.
- * `data_ptr` allows for default constructing FastApiCallbackOptions.
- */
- union {
- uintptr_t data_ptr;
- v8::Value data;
- };
- };
- namespace internal {
- // Helper to count the number of occurances of `T` in `List`
- template <typename T, typename... List>
- struct count : std::integral_constant<int, 0> {};
- template <typename T, typename... Args>
- struct count<T, T, Args...>
- : std::integral_constant<std::size_t, 1 + count<T, Args...>::value> {};
- template <typename T, typename U, typename... Args>
- struct count<T, U, Args...> : count<T, Args...> {};
- template <typename RetBuilder, typename... ArgBuilders>
- class CFunctionInfoImpl : public CFunctionInfo {
- static constexpr int kOptionsArgCount =
- count<FastApiCallbackOptions&, ArgBuilders...>();
- static constexpr int kReceiverCount = 1;
- static_assert(kOptionsArgCount == 0 || kOptionsArgCount == 1,
- "Only one options parameter is supported.");
- static_assert(sizeof...(ArgBuilders) >= kOptionsArgCount + kReceiverCount,
- "The receiver or the options argument is missing.");
- public:
- constexpr CFunctionInfoImpl()
- : CFunctionInfo(RetBuilder::Build(), sizeof...(ArgBuilders),
- arg_info_storage_),
- arg_info_storage_{ArgBuilders::Build()...} {
- constexpr CTypeInfo::Type kReturnType = RetBuilder::Build().GetType();
- static_assert(kReturnType == CTypeInfo::Type::kVoid ||
- kReturnType == CTypeInfo::Type::kBool ||
- kReturnType == CTypeInfo::Type::kInt32 ||
- kReturnType == CTypeInfo::Type::kUint32 ||
- kReturnType == CTypeInfo::Type::kFloat32 ||
- kReturnType == CTypeInfo::Type::kFloat64 ||
- kReturnType == CTypeInfo::Type::kAny,
- "64-bit int and api object values are not currently "
- "supported return types.");
- }
- private:
- const CTypeInfo arg_info_storage_[sizeof...(ArgBuilders)];
- };
- template <typename T>
- struct TypeInfoHelper {
- static_assert(sizeof(T) != sizeof(T), "This type is not supported");
- };
- #define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR(T, Enum) \
- template <> \
- struct TypeInfoHelper<T> { \
- static constexpr CTypeInfo::Flags Flags() { \
- return CTypeInfo::Flags::kNone; \
- } \
- \
- static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \
- static constexpr CTypeInfo::SequenceType SequenceType() { \
- return CTypeInfo::SequenceType::kScalar; \
- } \
- };
- template <CTypeInfo::Type type>
- struct CTypeInfoTraits {};
- #define DEFINE_TYPE_INFO_TRAITS(CType, Enum) \
- template <> \
- struct CTypeInfoTraits<CTypeInfo::Type::Enum> { \
- using ctype = CType; \
- };
- #define PRIMITIVE_C_TYPES(V) \
- V(bool, kBool) \
- V(int32_t, kInt32) \
- V(uint32_t, kUint32) \
- V(int64_t, kInt64) \
- V(uint64_t, kUint64) \
- V(float, kFloat32) \
- V(double, kFloat64)
- // Same as above, but includes deprecated types for compatibility.
- #define ALL_C_TYPES(V) \
- PRIMITIVE_C_TYPES(V) \
- V(void, kVoid) \
- V(v8::Local<v8::Value>, kV8Value) \
- V(v8::Local<v8::Object>, kV8Value) \
- V(AnyCType, kAny)
- // ApiObject was a temporary solution to wrap the pointer to the v8::Value.
- // Please use v8::Local<v8::Value> in new code for the arguments and
- // v8::Local<v8::Object> for the receiver, as ApiObject will be deprecated.
- ALL_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR)
- PRIMITIVE_C_TYPES(DEFINE_TYPE_INFO_TRAITS)
- #undef PRIMITIVE_C_TYPES
- #undef ALL_C_TYPES
- #define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA(T, Enum) \
- template <> \
- struct TypeInfoHelper<const FastApiTypedArray<T>&> { \
- static constexpr CTypeInfo::Flags Flags() { \
- return CTypeInfo::Flags::kNone; \
- } \
- \
- static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \
- static constexpr CTypeInfo::SequenceType SequenceType() { \
- return CTypeInfo::SequenceType::kIsTypedArray; \
- } \
- };
- #define TYPED_ARRAY_C_TYPES(V) \
- V(int32_t, kInt32) \
- V(uint32_t, kUint32) \
- V(int64_t, kInt64) \
- V(uint64_t, kUint64) \
- V(float, kFloat32) \
- V(double, kFloat64)
- TYPED_ARRAY_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA)
- #undef TYPED_ARRAY_C_TYPES
- template <>
- struct TypeInfoHelper<v8::Local<v8::Array>> {
- static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
- static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kVoid; }
- static constexpr CTypeInfo::SequenceType SequenceType() {
- return CTypeInfo::SequenceType::kIsSequence;
- }
- };
- template <>
- struct TypeInfoHelper<v8::Local<v8::Uint32Array>> {
- static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
- static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kUint32; }
- static constexpr CTypeInfo::SequenceType SequenceType() {
- return CTypeInfo::SequenceType::kIsTypedArray;
- }
- };
- template <>
- struct TypeInfoHelper<FastApiCallbackOptions&> {
- static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; }
- static constexpr CTypeInfo::Type Type() {
- return CTypeInfo::kCallbackOptionsType;
- }
- static constexpr CTypeInfo::SequenceType SequenceType() {
- return CTypeInfo::SequenceType::kScalar;
- }
- };
- #define STATIC_ASSERT_IMPLIES(COND, ASSERTION, MSG) \
- static_assert(((COND) == 0) || (ASSERTION), MSG)
- } // namespace internal
- template <typename T, CTypeInfo::Flags... Flags>
- class V8_EXPORT CTypeInfoBuilder {
- public:
- using BaseType = T;
- static constexpr CTypeInfo Build() {
- constexpr CTypeInfo::Flags kFlags =
- MergeFlags(internal::TypeInfoHelper<T>::Flags(), Flags...);
- constexpr CTypeInfo::Type kType = internal::TypeInfoHelper<T>::Type();
- constexpr CTypeInfo::SequenceType kSequenceType =
- internal::TypeInfoHelper<T>::SequenceType();
- STATIC_ASSERT_IMPLIES(
- uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kAllowSharedBit),
- (kSequenceType == CTypeInfo::SequenceType::kIsTypedArray ||
- kSequenceType == CTypeInfo::SequenceType::kIsArrayBuffer),
- "kAllowSharedBit is only allowed for TypedArrays and ArrayBuffers.");
- STATIC_ASSERT_IMPLIES(
- uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kEnforceRangeBit),
- CTypeInfo::IsIntegralType(kType),
- "kEnforceRangeBit is only allowed for integral types.");
- STATIC_ASSERT_IMPLIES(
- uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kClampBit),
- CTypeInfo::IsIntegralType(kType),
- "kClampBit is only allowed for integral types.");
- STATIC_ASSERT_IMPLIES(
- uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kIsRestrictedBit),
- CTypeInfo::IsFloatingPointType(kType),
- "kIsRestrictedBit is only allowed for floating point types.");
- STATIC_ASSERT_IMPLIES(kSequenceType == CTypeInfo::SequenceType::kIsSequence,
- kType == CTypeInfo::Type::kVoid,
- "Sequences are only supported from void type.");
- STATIC_ASSERT_IMPLIES(
- kSequenceType == CTypeInfo::SequenceType::kIsTypedArray,
- CTypeInfo::IsPrimitive(kType) || kType == CTypeInfo::Type::kVoid,
- "TypedArrays are only supported from primitive types or void.");
- // Return the same type with the merged flags.
- return CTypeInfo(internal::TypeInfoHelper<T>::Type(),
- internal::TypeInfoHelper<T>::SequenceType(), kFlags);
- }
- private:
- template <typename... Rest>
- static constexpr CTypeInfo::Flags MergeFlags(CTypeInfo::Flags flags,
- Rest... rest) {
- return CTypeInfo::Flags(uint8_t(flags) | uint8_t(MergeFlags(rest...)));
- }
- static constexpr CTypeInfo::Flags MergeFlags() { return CTypeInfo::Flags(0); }
- };
- namespace internal {
- template <typename RetBuilder, typename... ArgBuilders>
- class CFunctionBuilderWithFunction {
- public:
- explicit constexpr CFunctionBuilderWithFunction(const void* fn) : fn_(fn) {}
- template <CTypeInfo::Flags... Flags>
- constexpr auto Ret() {
- return CFunctionBuilderWithFunction<
- CTypeInfoBuilder<typename RetBuilder::BaseType, Flags...>,
- ArgBuilders...>(fn_);
- }
- template <unsigned int N, CTypeInfo::Flags... Flags>
- constexpr auto Arg() {
- // Return a copy of the builder with the Nth arg builder merged with
- // template parameter pack Flags.
- return ArgImpl<N, Flags...>(
- std::make_index_sequence<sizeof...(ArgBuilders)>());
- }
- auto Build() {
- static CFunctionInfoImpl<RetBuilder, ArgBuilders...> instance;
- return CFunction(fn_, &instance);
- }
- private:
- template <bool Merge, unsigned int N, CTypeInfo::Flags... Flags>
- struct GetArgBuilder;
- // Returns the same ArgBuilder as the one at index N, including its flags.
- // Flags in the template parameter pack are ignored.
- template <unsigned int N, CTypeInfo::Flags... Flags>
- struct GetArgBuilder<false, N, Flags...> {
- using type =
- typename std::tuple_element<N, std::tuple<ArgBuilders...>>::type;
- };
- // Returns an ArgBuilder with the same base type as the one at index N,
- // but merges the flags with the flags in the template parameter pack.
- template <unsigned int N, CTypeInfo::Flags... Flags>
- struct GetArgBuilder<true, N, Flags...> {
- using type = CTypeInfoBuilder<
- typename std::tuple_element<N,
- std::tuple<ArgBuilders...>>::type::BaseType,
- std::tuple_element<N, std::tuple<ArgBuilders...>>::type::Build()
- .GetFlags(),
- Flags...>;
- };
- // Return a copy of the CFunctionBuilder, but merges the Flags on
- // ArgBuilder index N with the new Flags passed in the template parameter
- // pack.
- template <unsigned int N, CTypeInfo::Flags... Flags, size_t... I>
- constexpr auto ArgImpl(std::index_sequence<I...>) {
- return CFunctionBuilderWithFunction<
- RetBuilder, typename GetArgBuilder<N == I, I, Flags...>::type...>(fn_);
- }
- const void* fn_;
- };
- class CFunctionBuilder {
- public:
- constexpr CFunctionBuilder() {}
- template <typename R, typename... Args>
- constexpr auto Fn(R (*fn)(Args...)) {
- return CFunctionBuilderWithFunction<CTypeInfoBuilder<R>,
- CTypeInfoBuilder<Args>...>(
- reinterpret_cast<const void*>(fn));
- }
- };
- } // namespace internal
- // static
- template <typename R, typename... Args>
- CFunction CFunction::ArgUnwrap<R (*)(Args...)>::Make(R (*func)(Args...)) {
- return internal::CFunctionBuilder().Fn(func).Build();
- }
- using CFunctionBuilder = internal::CFunctionBuilder;
- static constexpr CTypeInfo kTypeInfoInt32 = CTypeInfo(CTypeInfo::Type::kInt32);
- static constexpr CTypeInfo kTypeInfoFloat64 =
- CTypeInfo(CTypeInfo::Type::kFloat64);
- /**
- * Copies the contents of this JavaScript array to a C++ buffer with
- * a given max_length. A CTypeInfo is passed as an argument,
- * instructing different rules for conversion (e.g. restricted float/double).
- * The element type T of the destination array must match the C type
- * corresponding to the CTypeInfo (specified by CTypeInfoTraits).
- * If the array length is larger than max_length or the array is of
- * unsupported type, the operation will fail, returning false. Generally, an
- * array which contains objects, undefined, null or anything not convertible
- * to the requested destination type, is considered unsupported. The operation
- * returns true on success. `type_info` will be used for conversions.
- */
- template <const CTypeInfo* type_info, typename T>
- V8_DEPRECATED(
- "Use TryToCopyAndConvertArrayToCppBuffer<CTypeInfo::Identifier, T>()")
- bool V8_EXPORT V8_WARN_UNUSED_RESULT
- TryCopyAndConvertArrayToCppBuffer(Local<Array> src, T* dst,
- uint32_t max_length);
- template <>
- V8_DEPRECATED(
- "Use TryToCopyAndConvertArrayToCppBuffer<CTypeInfo::Identifier, T>()")
- inline bool V8_WARN_UNUSED_RESULT
- TryCopyAndConvertArrayToCppBuffer<&kTypeInfoInt32, int32_t>(
- Local<Array> src, int32_t* dst, uint32_t max_length) {
- return false;
- }
- template <>
- V8_DEPRECATED(
- "Use TryToCopyAndConvertArrayToCppBuffer<CTypeInfo::Identifier, T>()")
- inline bool V8_WARN_UNUSED_RESULT
- TryCopyAndConvertArrayToCppBuffer<&kTypeInfoFloat64, double>(
- Local<Array> src, double* dst, uint32_t max_length) {
- return false;
- }
- template <CTypeInfo::Identifier type_info_id, typename T>
- bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer(
- Local<Array> src, T* dst, uint32_t max_length);
- template <>
- bool V8_EXPORT V8_WARN_UNUSED_RESULT
- TryToCopyAndConvertArrayToCppBuffer<CTypeInfoBuilder<int32_t>::Build().GetId(),
- int32_t>(Local<Array> src, int32_t* dst,
- uint32_t max_length);
- template <>
- bool V8_EXPORT V8_WARN_UNUSED_RESULT
- TryToCopyAndConvertArrayToCppBuffer<CTypeInfoBuilder<uint32_t>::Build().GetId(),
- uint32_t>(Local<Array> src, uint32_t* dst,
- uint32_t max_length);
- template <>
- bool V8_EXPORT V8_WARN_UNUSED_RESULT
- TryToCopyAndConvertArrayToCppBuffer<CTypeInfoBuilder<float>::Build().GetId(),
- float>(Local<Array> src, float* dst,
- uint32_t max_length);
- template <>
- bool V8_EXPORT V8_WARN_UNUSED_RESULT
- TryToCopyAndConvertArrayToCppBuffer<CTypeInfoBuilder<double>::Build().GetId(),
- double>(Local<Array> src, double* dst,
- uint32_t max_length);
- } // namespace v8
- #endif // INCLUDE_V8_FAST_API_CALLS_H_
|