#pragma once #include #include #include "error.h" #include "handle_cast.h" namespace ivm { namespace detail { // Returns the name of the current function being called, used for error messages inline auto CalleeName(const v8::FunctionCallbackInfo& info) -> std::string { v8::Isolate* isolate = info.GetIsolate(); return std::string("`")+ *v8::String::Utf8Value{isolate, Unmaybe(info.Data()->ToString(isolate->GetCurrentContext()))}+ "`"; } inline auto CalleeName(const v8::PropertyCallbackInfo& info) -> std::string { v8::Isolate* isolate = info.GetIsolate(); return std::string("`")+ *v8::String::Utf8Value{isolate, Unmaybe(info.Data()->ToString(isolate->GetCurrentContext()))}+ "`"; } inline auto CalleeName(const v8::PropertyCallbackInfo& info) -> std::string { v8::Isolate* isolate = info.GetIsolate(); return std::string("`")+ *v8::String::Utf8Value{isolate, Unmaybe(info.Data()->ToString(isolate->GetCurrentContext()))}+ "`"; } // Specifies the types which may be default constructed when missing from arguments template struct IsEmptyHandleAllowed : std::false_type {}; template struct IsEmptyHandleAllowed : std::true_type {}; template struct IsEmptyHandleAllowed> : std::true_type {}; template struct IsEmptyHandleAllowed> : std::true_type {}; // Statically counts the required arguments in a given callback function template struct RequiredArguments; template struct RequiredArguments { constexpr static size_t value = std::max( IsEmptyHandleAllowed::value ? 0 : Index + 1, RequiredArguments::value ); }; template struct RequiredArguments { constexpr static size_t value = 0; }; // Extracts parameters from various v8 call signatures template inline auto ExtractParamImpl(const v8::FunctionCallbackInfo& info) -> v8::Local { if (Index == -2) { return info.Data(); } else if (Index == -1) { return info.This(); } else { return info[Index]; } } template inline auto ExtractParamImpl(v8::Local /*name*/, const v8::PropertyCallbackInfo& info) -> v8::Local { static_assert(Index == 0, "Getter callback should have no parameters"); return info.This(); } template inline auto ExtractParamImpl(v8::Local /*name*/, v8::Local value, const v8::PropertyCallbackInfo& info) -> v8::Local { static_assert(Index < 2, "Setter callback should have exactly 1 parameter"); if (Index == 0) { return info.This(); } else if (Index == 1) { return value; } } // Generic unpacker with empty checking template class ParamExtractor { public: explicit ParamExtractor(Args... args) : args{args...} {} template inline void CheckLength(std::tuple* /*tuple*/) { size_t length = CalculateLength(std::get(args)); constexpr size_t required = RequiredArguments<0, Types...>::value; if (length < required) { // `this` counts as a parameter so adjust this count for the error message constexpr int adjusted = static_cast(required) + std::max(Offset, -1); throw RuntimeTypeError{CalleeName()+ " requires at least "+ std::to_string(adjusted)+ (adjusted == 1 ? " parameter" : " parameters")}; } } template inline decltype(auto) Invoke() { constexpr int AdjustedIndex = Index == 0 ? Offset : (std::max(-1, Offset) + Index); ii = AdjustedIndex; v8::Local value = Extract(seq); return HandleCast(value, std::get(args)); } [[noreturn]] void Caught(const ParamIncorrect& ex) { if (ii == -1) { throw RuntimeTypeError{CalleeName()+ " requires `this` to be "+ ex.type}; } else { throw RuntimeTypeError{CalleeName()+ " requires parameter "+ std::to_string(ii + 1)+ " to be "+ ex.type}; } } private: auto CalleeName() { return detail::CalleeName(std::get(args)); } template inline auto Extract(std::index_sequence /*indices*/) { return ExtractParamImpl(std::get(args)...); } static inline auto CalculateLength(const v8::FunctionCallbackInfo& info) -> size_t { return info.Length() + (Offset == 0 ? 0 : 1); } static inline auto CalculateLength(const v8::PropertyCallbackInfo& /*info*/) -> size_t { return 1; // `this` } static inline auto CalculateLength(const v8::PropertyCallbackInfo& /*info*/) -> size_t { return 2; // `this`, `value` } std::tuple args; int ii = 0; static constexpr auto seq = std::make_index_sequence{}; }; } // namespace detail } // namespace ivm