Skip to content

Commit 08814ea

Browse files
committed
Store weak handle to v8::External along with external data (fix issue #30)
Added `external_data<T>` template class to store user data of type T when sizeof(T) > sizeof(void*) and weak handle to v8::External with value pointing to such an external_data instance. User data are stored in std::aligned_storage<sizeof T> data member of external_data instance and being deleted in the weak handle callback. Updated function, module, class, and property wrappers to accept universal references to binding entities to avoid making of unnecessary copies.
1 parent 59a4976 commit 08814ea

File tree

4 files changed

+96
-59
lines changed

4 files changed

+96
-59
lines changed

v8pp/class.hpp

+12-12
Original file line numberDiff line numberDiff line change
@@ -548,13 +548,12 @@ class class_
548548
}
549549

550550
/// Set static class function
551-
template<typename Function>
552-
typename std::enable_if<
553-
detail::is_callable<Function>::value, class_&>::type
554-
set(char const *name, Function func)
551+
template<typename Function, typename Fun = typename std::decay<Function>::type>
552+
typename std::enable_if<detail::is_callable<Fun>::value, class_&>::type
553+
set(char const *name, Function&& func)
555554
{
556555
class_singleton_.js_function_template()->Set(isolate(), name,
557-
wrap_function_template(isolate(), func));
556+
wrap_function_template(isolate(), std::forward<Fun>(func)));
558557
return *this;
559558
}
560559

@@ -573,7 +572,7 @@ class class_
573572
setter = nullptr;
574573
}
575574

576-
v8::Handle<v8::Value> data = detail::set_external_data(isolate(), attribute);
575+
v8::Handle<v8::Value> data = detail::set_external_data(isolate(), std::forward<Attribute>(attribute));
577576
v8::PropertyAttribute const prop_attrs = v8::PropertyAttribute(v8::DontDelete | (setter? 0 : v8::ReadOnly));
578577

579578
class_singleton_.class_function_template()->PrototypeTemplate()->SetAccessor(
@@ -585,18 +584,19 @@ class class_
585584
template<typename GetMethod, typename SetMethod>
586585
typename std::enable_if<std::is_member_function_pointer<GetMethod>::value
587586
&& std::is_member_function_pointer<SetMethod>::value, class_&>::type
588-
set(char const *name, property_<GetMethod, SetMethod> prop)
587+
set(char const *name, property_<GetMethod, SetMethod>&& prop)
589588
{
589+
using prop_type = property_<GetMethod, SetMethod>;
590590
v8::HandleScope scope(isolate());
591591

592-
v8::AccessorGetterCallback getter = property_<GetMethod, SetMethod>::get;
593-
v8::AccessorSetterCallback setter = property_<GetMethod, SetMethod>::set;
594-
if (property_<GetMethod, SetMethod>::is_readonly)
592+
v8::AccessorGetterCallback getter = prop_type::get;
593+
v8::AccessorSetterCallback setter = prop_type::set;
594+
if (prop_type::is_readonly)
595595
{
596596
setter = nullptr;
597597
}
598598

599-
v8::Handle<v8::Value> data = detail::set_external_data(isolate(), prop);
599+
v8::Handle<v8::Value> data = detail::set_external_data(isolate(), std::forward<prop_type>(prop));
600600
v8::PropertyAttribute const prop_attrs = v8::PropertyAttribute(v8::DontDelete | (setter? 0 : v8::ReadOnly));
601601

602602
class_singleton_.class_function_template()->PrototypeTemplate()->SetAccessor(v8pp::to_v8(isolate(), name),
@@ -606,7 +606,7 @@ class class_
606606

607607
/// Set value as a read-only property
608608
template<typename Value>
609-
class_& set_const(char const* name, Value value)
609+
class_& set_const(char const* name, Value const& value)
610610
{
611611
v8::HandleScope scope(isolate());
612612

v8pp/function.hpp

+68-33
Original file line numberDiff line numberDiff line change
@@ -49,36 +49,73 @@ union pointer_cast
4949
};
5050

5151
template<typename T>
52-
typename std::enable_if<is_pointer_cast_allowed<T>::value, v8::Local<v8::Value>>::type
53-
set_external_data(v8::Isolate* isolate, T value)
54-
{
55-
return v8::External::New(isolate, pointer_cast<T>(value));
56-
}
57-
58-
template<typename T>
59-
typename std::enable_if<!is_pointer_cast_allowed<T>::value, v8::Local<v8::Value>>::type
60-
set_external_data(v8::Isolate* isolate, T const& value)
52+
class external_data
6153
{
62-
T* data = new T(value);
63-
64-
v8::Local<v8::External> ext = v8::External::New(isolate, data);
54+
public:
55+
static v8::Local<v8::External> set(v8::Isolate* isolate, T&& data)
56+
{
57+
external_data* value = new external_data;
58+
try
59+
{
60+
new (value->storage()) T(std::forward<T>(data));
61+
}
62+
catch (...)
63+
{
64+
delete value;
65+
throw;
66+
}
6567

66-
v8::Persistent<v8::External> pext(isolate, ext);
67-
pext.SetWeak(data,
68+
v8::Local<v8::External> ext = v8::External::New(isolate, value);
69+
value->pext_.Reset(isolate, ext);
70+
value->pext_.SetWeak(value,
6871
#ifdef V8_USE_WEAK_CB_INFO
69-
[](v8::WeakCallbackInfo<T> const& data)
72+
[](v8::WeakCallbackInfo<external_data> const& data)
7073
#else
71-
[](v8::WeakCallbackData<v8::External, T> const& data)
74+
[](v8::WeakCallbackData<v8::External, external_data> const& data)
7275
#endif
7376
{
7477
delete data.GetParameter();
7578
}
7679
#ifdef V8_USE_WEAK_CB_INFO
77-
,v8::WeakCallbackType::kParameter
80+
, v8::WeakCallbackType::kParameter
7881
#endif
79-
);
82+
);
83+
return ext;
84+
}
85+
86+
static T& get(v8::Local<v8::External> ext)
87+
{
88+
external_data* value = static_cast<external_data*>(ext->Value());
89+
return *static_cast<T*>(value->storage());
90+
}
8091

81-
return ext;
92+
private:
93+
void* storage() { return &storage_; }
94+
~external_data()
95+
{
96+
if (!pext_.IsEmpty())
97+
{
98+
static_cast<T*>(storage())->~T();
99+
pext_.Reset();
100+
}
101+
}
102+
using data_storage = typename std::aligned_storage<sizeof(T)>::type;
103+
data_storage storage_;
104+
v8::UniquePersistent<v8::External> pext_;
105+
};
106+
107+
template<typename T>
108+
typename std::enable_if<is_pointer_cast_allowed<T>::value, v8::Local<v8::Value>>::type
109+
set_external_data(v8::Isolate* isolate, T value)
110+
{
111+
return v8::External::New(isolate, pointer_cast<T>(value));
112+
}
113+
114+
template<typename T>
115+
typename std::enable_if<!is_pointer_cast_allowed<T>::value, v8::Local<v8::Value>>::type
116+
set_external_data(v8::Isolate* isolate, T&& value)
117+
{
118+
return external_data<T>::set(isolate, std::forward<T>(value));
82119
}
83120

84121
template<typename T>
@@ -92,17 +129,15 @@ template<typename T>
92129
typename std::enable_if<!is_pointer_cast_allowed<T>::value, T&>::type
93130
get_external_data(v8::Handle<v8::Value> value)
94131
{
95-
T* data = static_cast<T*>(value.As<v8::External>()->Value());
96-
return *data;
132+
return external_data<T>::get(value.As<v8::External>());
97133
}
98134

99135
template<typename F>
100136
typename std::enable_if<is_callable<F>::value,
101137
typename function_traits<F>::return_type>::type
102138
invoke(v8::FunctionCallbackInfo<v8::Value> const& args)
103139
{
104-
F f = get_external_data<F>(args.Data());
105-
return call_from_v8(std::forward<F>(f), args);
140+
return call_from_v8(std::forward<F>(get_external_data<F>(args.Data())), args);
106141
}
107142

108143
template<typename F>
@@ -114,10 +149,8 @@ invoke(v8::FunctionCallbackInfo<v8::Value> const& args)
114149
static_assert(std::tuple_size<arguments>::value > 0, "");
115150
using class_type = typename std::tuple_element<0, arguments>::type;
116151

117-
F f = get_external_data<F>(args.Data());
118-
class_type& obj = from_v8<class_type&>(args.GetIsolate(), args.This());
119-
120-
return call_from_v8(obj, std::forward<F>(f), args);
152+
return call_from_v8(from_v8<class_type&>(args.GetIsolate(), args.This()),
153+
std::forward<F>(get_external_data<F>(args.Data())), args);
121154
}
122155

123156
template<typename F>
@@ -157,20 +190,22 @@ void forward_function(v8::FunctionCallbackInfo<v8::Value> const& args)
157190

158191
/// Wrap C++ function into new V8 function template
159192
template<typename F>
160-
v8::Handle<v8::FunctionTemplate> wrap_function_template(v8::Isolate* isolate, F func)
193+
v8::Handle<v8::FunctionTemplate> wrap_function_template(v8::Isolate* isolate, F&& func)
161194
{
162-
return v8::FunctionTemplate::New(isolate, &detail::forward_function<F>,
163-
detail::set_external_data(isolate, func));
195+
using F_type = typename std::decay<F>::type;
196+
return v8::FunctionTemplate::New(isolate, &detail::forward_function<F_type>,
197+
detail::set_external_data(isolate, std::forward<F_type>(func)));
164198
}
165199

166200
/// Wrap C++ function into new V8 function
167201
/// Set nullptr or empty string for name
168202
/// to make the function anonymous
169203
template<typename F>
170-
v8::Handle<v8::Function> wrap_function(v8::Isolate* isolate, char const* name, F func)
204+
v8::Handle<v8::Function> wrap_function(v8::Isolate* isolate, char const* name, F&& func)
171205
{
172-
v8::Handle<v8::Function> fn = v8::Function::New(isolate, &detail::forward_function<F>,
173-
detail::set_external_data(isolate, func));
206+
using F_type = typename std::decay<F>::type;
207+
v8::Handle<v8::Function> fn = v8::Function::New(isolate, &detail::forward_function<F_type>,
208+
detail::set_external_data(isolate, std::forward<F_type>(func)));
174209
if (name && *name)
175210
{
176211
fn->SetName(to_v8(isolate, name));

v8pp/module.hpp

+12-10
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,11 @@ class module
6464
}
6565

6666
/// Set a C++ function in the module with specified name
67-
template<typename Function>
68-
typename std::enable_if<detail::is_callable<Function>::value, module&>::type
69-
set(char const* name, Function func)
67+
template<typename Function, typename Fun = typename std::decay<Function>::type>
68+
typename std::enable_if<detail::is_callable<Fun>::value, module&>::type
69+
set(char const* name, Function&& func)
7070
{
71-
return set(name, wrap_function_template(isolate_, func));
71+
return set(name, wrap_function_template(isolate_, std::forward<Fun>(func)));
7272
}
7373

7474
/// Set a C++ variable in the module with specified name
@@ -94,18 +94,20 @@ class module
9494

9595
/// Set v8pp::property in the module with specified name
9696
template<typename GetFunction, typename SetFunction>
97-
module& set(char const *name, property_<GetFunction, SetFunction> prop)
97+
module& set(char const *name, property_<GetFunction, SetFunction>&& prop)
9898
{
99+
using prop_type = property_<GetFunction, SetFunction>;
100+
99101
v8::HandleScope scope(isolate_);
100102

101-
v8::AccessorGetterCallback getter = property_<GetFunction, SetFunction>::get;
102-
v8::AccessorSetterCallback setter = property_<GetFunction, SetFunction>::set;
103-
if (property_<GetFunction, SetFunction>::is_readonly)
103+
v8::AccessorGetterCallback getter = prop_type::get;
104+
v8::AccessorSetterCallback setter = prop_type::set;
105+
if (prop_type::is_readonly)
104106
{
105107
setter = nullptr;
106108
}
107109

108-
v8::Handle<v8::Value> data = detail::set_external_data(isolate_, prop);
110+
v8::Handle<v8::Value> data = detail::set_external_data(isolate_, std::forward<prop_type>(prop));
109111
v8::PropertyAttribute const prop_attrs = v8::PropertyAttribute(v8::DontDelete | (setter? 0 : v8::ReadOnly));
110112

111113
obj_->SetAccessor(v8pp::to_v8(isolate_, name), getter, setter, data, v8::DEFAULT, prop_attrs);
@@ -124,7 +126,7 @@ class module
124126

125127
/// Set a value convertible to JavaScript as a read-only property
126128
template<typename Value>
127-
module& set_const(char const* name, Value value)
129+
module& set_const(char const* name, Value const& value)
128130
{
129131
v8::HandleScope scope(isolate_);
130132

v8pp/property.hpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ struct r_property_impl<Get, Set, true>
135135

136136
class_type& obj = v8pp::from_v8<class_type&>(isolate, info.This());
137137

138-
Property prop = detail::get_external_data<Property>(info.Data());
138+
Property const& prop = detail::get_external_data<Property>(info.Data());
139139
assert(prop.get_);
140140

141141
if (prop.get_)
@@ -186,7 +186,7 @@ struct r_property_impl<Get, Set, false>
186186
{
187187
v8::Isolate* isolate = info.GetIsolate();
188188

189-
Property prop = detail::get_external_data<Property>(info.Data());
189+
Property const& prop = detail::get_external_data<Property>(info.Data());
190190
assert(prop.get_);
191191

192192
if (prop.get_)
@@ -246,7 +246,7 @@ struct rw_property_impl<Get, Set, true>
246246

247247
class_type& obj = v8pp::from_v8<class_type&>(isolate, info.This());
248248

249-
Property prop = detail::get_external_data<Property>(info.Data());
249+
Property const& prop = detail::get_external_data<Property>(info.Data());
250250
assert(prop.set_);
251251

252252
if (prop.set_)
@@ -297,7 +297,7 @@ struct rw_property_impl<Get, Set, false>
297297
{
298298
v8::Isolate* isolate = info.GetIsolate();
299299

300-
Property prop = detail::get_external_data<Property>(info.Data());
300+
Property const& prop = detail::get_external_data<Property>(info.Data());
301301
assert(prop.set_);
302302

303303
if (prop.set_)

0 commit comments

Comments
 (0)