From ea1db83790121b67c9eb863ae6a8298b15f0909e Mon Sep 17 00:00:00 2001 From: Yonatan Striem-Amit Date: Wed, 11 Dec 2019 16:06:34 -0500 Subject: [PATCH 1/8] Support for member's properties and cost member access --- v8pp/class.hpp | 64 ++++++++++++++++- v8pp/property.hpp | 172 ++++++++++++++++++++++++++++++++++++++++++++++ v8pp/throw_ex.ipp | 4 +- 3 files changed, 235 insertions(+), 5 deletions(-) diff --git a/v8pp/class.hpp b/v8pp/class.hpp index 6345f725..083c00e6 100644 --- a/v8pp/class.hpp +++ b/v8pp/class.hpp @@ -260,7 +260,7 @@ class class_ /// Set class member data template typename std::enable_if< - std::is_member_object_pointer::value, class_&>::type + std::is_member_object_pointer::value, class_&>::type set(char const *name, Attribute attribute, bool readonly = false) { v8::HandleScope scope(isolate()); @@ -283,7 +283,7 @@ class class_ return *this; } - /// Set read/write class property with getter and setter + /// Set read/write class property with getter and setter template typename std::enable_if::value && std::is_member_function_pointer::value, class_&>::type @@ -296,6 +296,8 @@ class class_ typename detail::function_traits::template pointer_type >; property_type prop(property); + + // Need to generate one that chains property getter with the member getter below v8::AccessorGetterCallback getter = property_type::template get; v8::AccessorSetterCallback setter = property_type::template set; if (prop.is_readonly) @@ -311,9 +313,65 @@ class class_ return *this; } + template + typename std::enable_if::value + && std::is_member_function_pointer::value + && std::is_member_function_pointer::value, class_&>::type + set(char const *name, member_property_ &&property) { + using attribute_type = typename + detail::function_traits::template pointer_type; + + using property_type = member_property_< + Attribute, + GetMethod, + SetMethod + //typename detail::function_traits::template pointer_type<>, + //typename detail::function_traits::template pointer_type<> + >; + property_type prop(property); + v8::AccessorGetterCallback getter = property_type::template get; + v8::AccessorSetterCallback setter = property_type::template set; + + if (prop.is_readonly) + { + setter = nullptr; + } + + class_info_.class_function_template()->PrototypeTemplate() + ->SetAccessor(v8pp::to_v8(isolate(), name), getter, setter, + detail::set_external_data(isolate(), + std::forward(prop)), v8::DEFAULT, + v8::PropertyAttribute(v8::DontDelete | (setter ? 0 : v8::ReadOnly))); + return *this; + + } + + /// Set class member data + template + typename std::enable_if< + std::is_member_object_pointer::value, class_&>::type + set_const(char const *name, Attribute attribute) + { + v8::HandleScope scope(isolate()); + + using attribute_type = typename + detail::function_traits::template pointer_type; + attribute_type attr(attribute); + v8::AccessorGetterCallback getter = &member_get; + + class_info_.class_function_template()->PrototypeTemplate() + ->SetAccessor(v8pp::to_v8(isolate(), name), getter, nullptr, + detail::set_external_data(isolate(), + std::forward(attr)), v8::DEFAULT, + v8::PropertyAttribute(v8::DontDelete | v8::ReadOnly)); + return *this; + } + /// Set value as a read-only property template - class_& set_const(char const* name, Value const& value) + typename std::enable_if< + !std::is_member_object_pointer::value, class_&>::type + set_const(char const* name, Value const& value) { v8::HandleScope scope(isolate()); diff --git a/v8pp/property.hpp b/v8pp/property.hpp index 9cdcdb82..aa3d8602 100644 --- a/v8pp/property.hpp +++ b/v8pp/property.hpp @@ -19,6 +19,9 @@ namespace v8pp { template struct property_; +template +struct member_property_; + namespace detail { struct getter_tag {}; @@ -53,6 +56,14 @@ template using is_setter = std::integral_constant::arg_count == 1 && is_void_return::value>; +template +using is_loose_setter = std::integral_constant::arg_count == 1 && + std::is_member_function_pointer::value && + std::is_same< + typename std::decay::return_type>::type, + typename std::decay::arguments>::type>::type>::value>; + template using is_direct_setter = std::integral_constant::arg_count == 3 && @@ -318,6 +329,115 @@ struct rw_property_impl } }; +template +struct rw_member_property { + using property_type = member_property_; + + using class_type = typename std::decay::arguments> ::type>::type; + + static_assert(std::is_member_pointer::value, "member property must be `&T::member`"); + + static_assert(is_getter::value + || is_direct_getter::value + || is_isolate_getter::value, + "property get function must be either `T ()` or \ + `void (v8::Local name, v8::PropertyCallbackInfo const& info)` or \ + `T (v8::Isolate*)`"); + + static void get_impl(class_type& obj, Attribute attr, Get get, v8::Local, + v8::PropertyCallbackInfo const& info, getter_tag) + { + info.GetReturnValue().Set(to_v8(info.GetIsolate(), ((obj.*attr).*get)())); + } + + static void get_impl(class_type& obj, Attribute attr, Get get, + v8::Local name, v8::PropertyCallbackInfo const& info, + direct_getter_tag) + { + ((obj.*attr).*get)(name, info); + } + + static void get_impl(class_type& obj, Attribute attr, Get get, v8::Local, + v8::PropertyCallbackInfo const& info, isolate_getter_tag) + { + v8::Isolate* isolate = info.GetIsolate(); + + info.GetReturnValue().Set(to_v8(isolate, ((obj.*attr).*get)(isolate))); + } + + template + static void get(v8::Local name, + v8::PropertyCallbackInfo const& info) + try + { + static_assert(std::is_member_pointer::value,"XXX"); + auto obj = v8pp::class_::unwrap_object(info.GetIsolate(), info.This()); + assert(obj); + + property_type const& prop = detail::get_external_data(info.Data()); + assert(prop.getter); + assert(prop.attr); + + if (obj && prop.getter && prop.attr) + { + get_impl(*obj, prop.attr, prop.getter, name, info, select_getter_tag()); + } + } + catch (std::exception const& ex) + { + info.GetReturnValue().Set(throw_ex(info.GetIsolate(), ex.what())); + } + + static void set_impl(class_type& obj, Attribute attr, Set set, v8::Local, + v8::Local value, v8::PropertyCallbackInfo const& info, + setter_tag) + { + using value_type = typename call_from_v8_traits::template arg_type<0>; + + ((obj.*attr).*set)(v8pp::from_v8(info.GetIsolate(), value)); + } + + static void set_impl(class_type& obj, Attribute attr, Set set, v8::Local name, + v8::Local value, v8::PropertyCallbackInfo const& info, + direct_setter_tag) + { + ((obj.*attr).*set)(name, value, info); + } + + static void set_impl(class_type& obj, Attribute attr, Set set, v8::Local, + v8::Local value, v8::PropertyCallbackInfo const& info, + isolate_setter_tag) + { + using value_type = typename call_from_v8_traits::template arg_type<1>; + + v8::Isolate* isolate = info.GetIsolate(); + + ((obj.*attr).*set)(isolate, v8pp::from_v8(isolate, value)); + } + + template + static void set(v8::Local name, v8::Local value, + v8::PropertyCallbackInfo const& info) + try + { + auto obj = v8pp::class_::unwrap_object(info.GetIsolate(), info.This()); + assert(obj); + + property_type const& prop = detail::get_external_data(info.Data()); + assert(prop.setter); + + if (obj && prop.setter) + { + set_impl(*obj, prop.attr, prop.setter, name, value, info, select_setter_tag()); + } + } + catch (std::exception const& ex) + { + info.GetReturnValue().Set(throw_ex(info.GetIsolate(), ex.what())); + } + +}; } // namespace detail /// Property with get and set functions @@ -386,6 +506,53 @@ struct property_ } }; +/// Property with get and set functions +template +struct member_property_ + : detail::rw_member_property + { + static_assert(std::is_member_pointer::value, "member property must be `&T::member`"); + + static_assert(detail::is_getter::value + || detail::is_direct_getter::value + || detail::is_isolate_getter::value, + "property get function must be either `T ()` or " + "`void (v8::Local name, v8::PropertyCallbackInfo const& info)` or " + "`T (v8::Isolate*)`"); + + static_assert(detail::is_loose_setter::value, "wtf?"); + + static_assert(detail::is_setter::value + || detail::is_direct_setter::value + || detail::is_isolate_setter::value + || detail::is_loose_setter::value, + "property set function must be either `void (T)` or \ + `void (v8::Local name, v8::Local value, v8::PropertyCallbackInfo const& info)` or \ + `void (v8::Isolate*, T)` or \ + `T& (T::()(const T2&)`" + ); + + Attribute attr; + Get getter; + Set setter; + + enum { is_readonly = false }; + + member_property_(Attribute attr, Get getter, Set setter) + : attr(attr) + , getter(getter) + , setter(setter) + { + } + + template + member_property_(member_property_ const& other) + : attr(other.attr) + , getter(other.getter) + , setter(other.setter) + { + } + }; /// Create read/write property from get and set member functions template property_ property(Get get, Set set) @@ -400,6 +567,11 @@ property_ property(Get get) return property_(get); } +template +member_property_ property(Attribute attr, Get get, Set set) { + return member_property_(attr, get, set); +} + } // namespace v8pp #endif // V8PP_PROPERTY_HPP_INCLUDED diff --git a/v8pp/throw_ex.ipp b/v8pp/throw_ex.ipp index 4c1738cb..b7b0371d 100644 --- a/v8pp/throw_ex.ipp +++ b/v8pp/throw_ex.ipp @@ -4,13 +4,13 @@ namespace v8pp { V8PP_IMPL v8::Local throw_ex(v8::Isolate* isolate, char const* str) { - return isolate->ThrowException(v8::String::NewFromUtf8(isolate, str)); + return isolate->ThrowException(v8::String::NewFromUtf8(isolate, str).FromMaybe(v8::Local())); } V8PP_IMPL v8::Local throw_ex(v8::Isolate* isolate, char const* str, v8::Local (*exception_ctor)(v8::Local)) { - return isolate->ThrowException(exception_ctor(v8::String::NewFromUtf8(isolate, str))); + return isolate->ThrowException(exception_ctor(v8::String::NewFromUtf8(isolate, str).FromMaybe(v8::Local()))); } } // namespace v8pp From ce1b8ce88208466da5aaf0640e1ba5f90b6d85ab Mon Sep 17 00:00:00 2001 From: Yonatan Striem-Amit Date: Wed, 11 Dec 2019 16:16:25 -0500 Subject: [PATCH 2/8] Normalize some tabs --- v8pp/class.hpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/v8pp/class.hpp b/v8pp/class.hpp index 083c00e6..85a2ca22 100644 --- a/v8pp/class.hpp +++ b/v8pp/class.hpp @@ -260,7 +260,7 @@ class class_ /// Set class member data template typename std::enable_if< - std::is_member_object_pointer::value, class_&>::type + std::is_member_object_pointer::value, class_&>::type set(char const *name, Attribute attribute, bool readonly = false) { v8::HandleScope scope(isolate()); @@ -283,7 +283,7 @@ class class_ return *this; } - /// Set read/write class property with getter and setter + /// Set read/write class property with getter and setter template typename std::enable_if::value && std::is_member_function_pointer::value, class_&>::type @@ -296,8 +296,6 @@ class class_ typename detail::function_traits::template pointer_type >; property_type prop(property); - - // Need to generate one that chains property getter with the member getter below v8::AccessorGetterCallback getter = property_type::template get; v8::AccessorSetterCallback setter = property_type::template set; if (prop.is_readonly) From d7894c4c0a04ca97003c2d6064ea2aced1815121 Mon Sep 17 00:00:00 2001 From: Yonatan Striem-Amit Date: Wed, 11 Dec 2019 16:17:45 -0500 Subject: [PATCH 3/8] Normalize some tabs --- v8pp/class.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v8pp/class.hpp b/v8pp/class.hpp index 85a2ca22..faa133f5 100644 --- a/v8pp/class.hpp +++ b/v8pp/class.hpp @@ -283,7 +283,7 @@ class class_ return *this; } - /// Set read/write class property with getter and setter + /// Set read/write class property with getter and setter template typename std::enable_if::value && std::is_member_function_pointer::value, class_&>::type From f22a39360fb15b01648bd9e2666be01edcb1ddae Mon Sep 17 00:00:00 2001 From: Yonatan Striem-Amit Date: Wed, 11 Dec 2019 16:19:52 -0500 Subject: [PATCH 4/8] Made tests compile --- v8pp/throw_ex.ipp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v8pp/throw_ex.ipp b/v8pp/throw_ex.ipp index b7b0371d..29ec54b0 100644 --- a/v8pp/throw_ex.ipp +++ b/v8pp/throw_ex.ipp @@ -4,13 +4,13 @@ namespace v8pp { V8PP_IMPL v8::Local throw_ex(v8::Isolate* isolate, char const* str) { - return isolate->ThrowException(v8::String::NewFromUtf8(isolate, str).FromMaybe(v8::Local())); + return isolate->ThrowException(v8::String::NewFromUtf8(isolate, str).ToLocalChecked()); } V8PP_IMPL v8::Local throw_ex(v8::Isolate* isolate, char const* str, v8::Local (*exception_ctor)(v8::Local)) { - return isolate->ThrowException(exception_ctor(v8::String::NewFromUtf8(isolate, str).FromMaybe(v8::Local()))); + return isolate->ThrowException(exception_ctor(v8::String::NewFromUtf8(isolate, str).ToLocalChecked())); } } // namespace v8pp From 3b3a5f95853a8023748c500dad3cd39cd1bd3060 Mon Sep 17 00:00:00 2001 From: Yonatan Striem-Amit Date: Wed, 11 Dec 2019 19:04:09 -0500 Subject: [PATCH 5/8] Removed debug assert --- v8pp/property.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/v8pp/property.hpp b/v8pp/property.hpp index aa3d8602..4f5d0965 100644 --- a/v8pp/property.hpp +++ b/v8pp/property.hpp @@ -371,7 +371,6 @@ struct rw_member_property { v8::PropertyCallbackInfo const& info) try { - static_assert(std::is_member_pointer::value,"XXX"); auto obj = v8pp::class_::unwrap_object(info.GetIsolate(), info.This()); assert(obj); From b2b09586ea68a561d65e2bfa742e029179fe8ffd Mon Sep 17 00:00:00 2001 From: Yonatan Striem-Amit Date: Fri, 13 Dec 2019 19:03:33 -0500 Subject: [PATCH 6/8] Allow access to context to work on promises --- v8pp/context.cpp | 6 ++++++ v8pp/context.hpp | 2 ++ 2 files changed, 8 insertions(+) diff --git a/v8pp/context.cpp b/v8pp/context.cpp index 6fd92f17..a1fd045c 100644 --- a/v8pp/context.cpp +++ b/v8pp/context.cpp @@ -273,4 +273,10 @@ v8::Local context::run_script(std::string const& source, return scope.Escape(result); } + v8::Local context::impl() { + v8::EscapableHandleScope handleScope(isolate_); + v8::Local ret = v8::Local::New(isolate_, impl_); + return handleScope.Escape(ret); + } + } // namespace v8pp diff --git a/v8pp/context.hpp b/v8pp/context.hpp index f4084f8f..2b297d11 100644 --- a/v8pp/context.hpp +++ b/v8pp/context.hpp @@ -68,6 +68,8 @@ class context return set(name, cl.js_function_template()->GetFunction(isolate_->GetCurrentContext()).ToLocalChecked()); } + v8::Local impl(); + private: bool own_isolate_; v8::Isolate* isolate_; From c61340c2d1e46cfc611f7e614577a3e4fbeaadae Mon Sep 17 00:00:00 2001 From: Yonatan Striem-Amit Date: Sat, 14 Dec 2019 08:48:13 -0500 Subject: [PATCH 7/8] #if for older version of nodes --- v8pp/throw_ex.ipp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/v8pp/throw_ex.ipp b/v8pp/throw_ex.ipp index 29ec54b0..0b1f419d 100644 --- a/v8pp/throw_ex.ipp +++ b/v8pp/throw_ex.ipp @@ -4,13 +4,21 @@ namespace v8pp { V8PP_IMPL v8::Local throw_ex(v8::Isolate* isolate, char const* str) { +#if (V8_MAJOR_VERSION > 7) || (V8_MAJOR_VERSION == 7 && V8_MINOR_VERSION >= 1) return isolate->ThrowException(v8::String::NewFromUtf8(isolate, str).ToLocalChecked()); +#else + return isolate->ThrowException(v8::String::NewFromUtf8(isolate, str)); +#endif } V8PP_IMPL v8::Local throw_ex(v8::Isolate* isolate, char const* str, v8::Local (*exception_ctor)(v8::Local)) { - return isolate->ThrowException(exception_ctor(v8::String::NewFromUtf8(isolate, str).ToLocalChecked())); +#if (V8_MAJOR_VERSION > 7) || (V8_MAJOR_VERSION == 7 && V8_MINOR_VERSION >= 1) + return isolate->ThrowException(exception_ctor(v8::String::NewFromUtf8(isolate, str).ToLocalChecked())); +#else + return isolate->ThrowException(exception_ctor(v8::String::NewFromUtf8(isolate, str))); +#endif } } // namespace v8pp From a2cc360767586e567505880799da2dd2f7a95987 Mon Sep 17 00:00:00 2001 From: Yonatan Striem-Amit Date: Sat, 14 Dec 2019 08:56:57 -0500 Subject: [PATCH 8/8] #if for older version of nodes with higher version of V8 when old StringFromUtf8 was deprecated --- v8pp/throw_ex.ipp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v8pp/throw_ex.ipp b/v8pp/throw_ex.ipp index 0b1f419d..507a279e 100644 --- a/v8pp/throw_ex.ipp +++ b/v8pp/throw_ex.ipp @@ -4,7 +4,7 @@ namespace v8pp { V8PP_IMPL v8::Local throw_ex(v8::Isolate* isolate, char const* str) { -#if (V8_MAJOR_VERSION > 7) || (V8_MAJOR_VERSION == 7 && V8_MINOR_VERSION >= 1) +#if (V8_MAJOR_VERSION > 7) || (V8_MAJOR_VERSION == 7 && V8_MINOR_VERSION >= 6) return isolate->ThrowException(v8::String::NewFromUtf8(isolate, str).ToLocalChecked()); #else return isolate->ThrowException(v8::String::NewFromUtf8(isolate, str)); @@ -14,7 +14,7 @@ V8PP_IMPL v8::Local throw_ex(v8::Isolate* isolate, char const* str) V8PP_IMPL v8::Local throw_ex(v8::Isolate* isolate, char const* str, v8::Local (*exception_ctor)(v8::Local)) { -#if (V8_MAJOR_VERSION > 7) || (V8_MAJOR_VERSION == 7 && V8_MINOR_VERSION >= 1) +#if (V8_MAJOR_VERSION > 7) || (V8_MAJOR_VERSION == 7 && V8_MINOR_VERSION >= 6) return isolate->ThrowException(exception_ctor(v8::String::NewFromUtf8(isolate, str).ToLocalChecked())); #else return isolate->ThrowException(exception_ctor(v8::String::NewFromUtf8(isolate, str)));