#pragma once #include #include "error.h" #include "../v8_version.h" namespace ivm { // Internal handle conversion error. All these `HandleCastImpl` functions are templated and inlined and // throwing generates verbose asm so this is implementated as a static function to clean up the // typical case struct ParamIncorrect : std::exception { explicit ParamIncorrect(const char* type) : type{type} {} [[noreturn]] static void Throw(const char* type) { throw ParamIncorrect{type}; } const char* type; }; // Common arguments for casting functions class HandleCastArguments { private: // Most of the time we won't need the context (and it is never needed in "strict" casting mode). // This little holder will only call GetCurrentContext() when needed because the optimizer can't // elide the call in any circumstances class ContextHolder { public: explicit ContextHolder(v8::Isolate* isolate) : isolate{isolate} {} inline operator v8::Local() const { // NOLINT(hicpp-explicit-conversions) if (context.IsEmpty()) { context = isolate->GetCurrentContext(); } return context; } private: v8::Isolate* const isolate; mutable v8::Local context{}; }; public: HandleCastArguments() : HandleCastArguments{true, v8::Isolate::GetCurrent()} {} HandleCastArguments(bool strict, v8::Isolate* isolate) : isolate{isolate}, context{isolate}, strict{strict} {} HandleCastArguments(const v8::FunctionCallbackInfo& info) : // NOLINT(hicpp-explicit-conversions) HandleCastArguments{true, info.GetIsolate()} {} HandleCastArguments(const v8::PropertyCallbackInfo& info) : // NOLINT(hicpp-explicit-conversions) HandleCastArguments{true, info.GetIsolate()} {} HandleCastArguments(const v8::PropertyCallbackInfo& info) : // NOLINT(hicpp-explicit-conversions) HandleCastArguments{true, info.GetIsolate()} {} v8::Isolate* const isolate; const ContextHolder context; const bool strict; }; // Helper template struct HandleCastTag {}; // Explicit casts: printf("%d\n", HandleCastImpl(value)); template auto HandleCast(Value&& value, HandleCastArguments arguments = {}) -> Type { return HandleCastImpl(std::forward(value), arguments, HandleCastTag{}); } // Identity cast template inline auto HandleCastImpl(Type value, const HandleCastArguments& /*arguments*/, HandleCastTag /*tag*/) { return value; } // Local -> Local<...> conversions inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag> /*tag*/) { if (value->IsArray()) { return value.As(); } ParamIncorrect::Throw("an array"); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& arguments, HandleCastTag> /*tag*/) { if (value->IsBoolean()) { return value.As(); } else if (!arguments.strict) { return value->ToBoolean(arguments.isolate); } ParamIncorrect::Throw("a boolean"); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag> /*tag*/) { if (value->IsFunction()) { return value.As(); } ParamIncorrect::Throw("a function"); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag> /*tag*/) { if (value->IsInt32()) { return value.As(); } ParamIncorrect::Throw("a 32-bit number"); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag> /*tag*/) { if (value->IsUint32()) { return value.As(); } ParamIncorrect::Throw("a 32-bit number"); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag> /*tag*/) { if (value->IsNumber()) { return value.As(); } ParamIncorrect::Throw("a number"); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag> /*tag*/) { if (value->IsObject()) { return value.As(); } ParamIncorrect::Throw("an object"); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& arguments, HandleCastTag> /*tag*/) { if (value->IsString()) { return value.As(); } else if (!arguments.strict) { return Unmaybe(value->ToString(arguments.context)); } ParamIncorrect::Throw("a string"); } // Local -> MaybeLocal<...> conversions inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag> /*tag*/) -> v8::MaybeLocal { if (value->IsNullOrUndefined()) { return {}; } else if (value->IsArray()) { return {value.As()}; } ParamIncorrect::Throw("an array"); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag> /*tag*/) -> v8::MaybeLocal { if (value->IsNullOrUndefined()) { return {}; } else if (value->IsFunction()) { return {value.As()}; } ParamIncorrect::Throw("a function"); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag> /*tag*/) -> v8::MaybeLocal { if (value->IsNullOrUndefined()) { return {}; } else if (value->IsObject()) { return {value.As()}; } ParamIncorrect::Throw("an object"); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& arguments, HandleCastTag> /*tag*/) -> v8::MaybeLocal { if (value->IsNullOrUndefined()) { return {}; } else if (value->IsString()) { return {value.As()}; } else if (!arguments.strict) { return {Unmaybe(value->ToString(arguments.context))}; } ParamIncorrect::Throw("a string"); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag> /*tag*/) { return v8::MaybeLocal{value}; } // Local<...> -> native C++ conversions inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& arguments, HandleCastTag /*tag*/) { return HandleCast(HandleCast>(value, arguments), arguments); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag /*tag*/) { return value->Value(); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& arguments, HandleCastTag /*tag*/) { return HandleCast(HandleCast>(value, arguments), arguments); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag /*tag*/) { return value->Value(); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& arguments, HandleCastTag /*tag*/) { return HandleCast(HandleCast>(value, arguments), arguments); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag /*tag*/) { return value->Value(); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& arguments, HandleCastTag /*tag*/) { return HandleCast(HandleCast>(value, arguments), arguments); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& /*arguments*/, HandleCastTag /*tag*/) { return value->Value(); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& arguments, HandleCastTag /*tag*/) { return HandleCast(HandleCast>(value, arguments), arguments); } inline auto HandleCastImpl(v8::Local value, const HandleCastArguments& arguments, HandleCastTag /*tag*/) { v8::String::Utf8Value utf8_value{arguments.isolate, value}; return std::string{*utf8_value, static_cast(utf8_value.length())}; } // native C++ -> Local conversions inline auto HandleCastImpl(bool value, const HandleCastArguments& arguments, HandleCastTag> /*tag*/) { return v8::Boolean::New(arguments.isolate, value); } inline auto HandleCastImpl(int32_t value, const HandleCastArguments& arguments, HandleCastTag> /*tag*/) { return v8::Integer::New(arguments.isolate, value); } inline auto HandleCastImpl(uint32_t value, const HandleCastArguments& arguments, HandleCastTag> /*tag*/) { return v8::Integer::NewFromUnsigned(arguments.isolate, value); } inline auto HandleCastImpl(int64_t value, const HandleCastArguments& arguments, HandleCastTag> /*tag*/) { return v8::BigInt::New(arguments.isolate, value); } inline auto HandleCastImpl(uint64_t value, const HandleCastArguments& arguments, HandleCastTag> /*tag*/) { return v8::BigInt::NewFromUnsigned(arguments.isolate, value); } inline auto HandleCastImpl(double value, const HandleCastArguments& arguments, HandleCastTag> /*tag*/) { return v8::Number::New(arguments.isolate, value); } inline auto HandleCastImpl(const char* value, const HandleCastArguments& arguments, HandleCastTag> /*tag*/) { return Unmaybe(v8::String::NewFromUtf8(arguments.isolate, value, v8::NewStringType::kNormal)); } inline auto HandleCastImpl(const std::string& value, const HandleCastArguments& arguments, HandleCastTag> /*tag*/) { return Unmaybe(v8::String::NewFromUtf8(arguments.isolate, value.c_str(), v8::NewStringType::kNormal, value.size())); } } // namespace ivm