15 #define USE_EASTL_FOR_INTERNALS 17 #ifdef USE_EASTL_FOR_INTERNALS 18 #include <EASTL/vector_map.h> 19 template<
class... ArgTs>
20 using MapT=eastl::vector_map<ArgTs...>;
23 template<
class... ArgTs>
24 using MapT=std::map<ArgTs...>;
35 #ifdef V8TOOLKIT_BIDIRECTIONAL_ENABLED 36 #define V8_CLASS_WRAPPER_HAS_BIDIRECTIONAL_SUPPORT 44 #ifdef V8_CLASS_WRAPPER_DEBUG 45 #define V8TOOLKIT_DEBUG(format_string, ...) \ 46 fprintf(stderr, format_string, ##__VA_ARGS__); 48 #define V8TOOLKIT_DEBUG(format_string, ...) 74 virtual void operator()(v8::Isolate * isolate,
const void *
object)
const = 0;
77 virtual bool destructive()
const = 0;
91 void operator()(v8::Isolate * isolate,
const void * void_object)
const override {
92 T*
object = (T*)void_object;
93 V8TOOLKIT_DEBUG(
"Deleting underlying C++ object at %p during V8 garbage collection\n", void_object);
95 isolate->AdjustAmountOfExternalAllocatedMemory(-static_cast<int64_t>(
sizeof(T)));
102 return fmt::format(
"_delete");
116 void operator()(v8::Isolate * isolate,
const void * void_object)
const override {
117 V8TOOLKIT_DEBUG(
"Not deleting underlying C++ object %p during V8 garbage collection\n", void_object);
125 return fmt::format(
"_leavealone");
142 virtual T * check(
AnyBase *,
bool first_call =
true 159 template<
class,
class,
class =
void>
170 template<
class T,
class Head,
class... Tail>
178 template<
class T,
class Head,
class... Tail>
189 template<
class,
class,
class =
void>
201 ANYBASE_PRINT(
"Failed to find match for anybase ({}) with type string: {}", demangle<T>(), any_base->
type_name);
208 template<
class T,
class Head,
class... Tail>
210 std::enable_if_t<!std::is_const<T>::value && std::is_const<Head>::value>> :
public TypeChecker<T, TypeList<Tail...>> {
216 virtual T *
check(
AnyBase * any_base,
bool first_call =
true)
const override {
217 ANYBASE_PRINT(
"In Type Checker<{}> Const mismatch: {} (string: {})", demangle<T>(), demangle<Head>(), any_base->
type_name);
218 if (
dynamic_cast<AnyPtr<Head> *
>(any_base) !=
nullptr) {
219 ANYBASE_PRINT(
"Not a match, but the value is a const version of goal type! {} vs {} Should you be casting to a const type instead?", demangle<T>(), demangle<Head>());
221 return SUPER::check(any_base);
229 template<
class T,
class Head,
class... Tail>
233 std::enable_if_t<std::is_const<T>::value ||
234 !std::is_const<Head>::value>
241 virtual T * check(
AnyBase * any_base,
bool first_call =
true)
const override;
254 std::map<v8::Isolate *, std::vector<func::function<void()>>> isolate_to_callback_map;
258 this->isolate_to_callback_map[isolate].push_back(callback);
262 for (
auto & callback : this->isolate_to_callback_map[isolate]) {
265 this->isolate_to_callback_map.erase(isolate);
276 #define V8TOOLKIT_V8CLASSWRAPPER_NO_POINTER_NO_REFERENCE_SFINAE !std::is_pointer<T>::value && !std::is_reference<T>::value 278 #define V8TOOLKIT_V8CLASSWRAPPER_FULL_TEMPLATE_SFINAE_PREFIX std::is_base_of<WrappedClassBase, T>::value 290 #ifdef TEST_NO_REAL_WRAPPERS 292 #define V8TOOLKIT_V8CLASSWRAPPER_USE_REAL_TEMPLATE_SFINAE std::enable_if_t<std::is_same<T, void>::value> 293 #define V8TOOLKIT_V8CLASSWRAPPER_USE_FAKE_TEMPLATE_SFINAE std::enable_if_t<!std::is_same<T, void>::value> 297 #define V8TOOLKIT_V8CLASSWRAPPER_USE_REAL_TEMPLATE_SFINAE std::enable_if_t<(V8TOOLKIT_V8CLASSWRAPPER_NO_POINTER_NO_REFERENCE_SFINAE) && \ 298 (V8TOOLKIT_V8CLASSWRAPPER_FULL_TEMPLATE_SFINAE_PREFIX)> 301 #define V8TOOLKIT_V8CLASSWRAPPER_USE_FAKE_TEMPLATE_SFINAE std::enable_if_t<(V8TOOLKIT_V8CLASSWRAPPER_NO_POINTER_NO_REFERENCE_SFINAE) && \ 302 !(V8TOOLKIT_V8CLASSWRAPPER_FULL_TEMPLATE_SFINAE_PREFIX)> 325 template<
class T,
class =
void>
326 class V8ClassWrapper;
331 using ConstT = std::add_const_t<T>;
360 std::vector<AttributeAdder> member_adders;
363 std::vector<StaticMethodAdder> static_method_adders;
367 std::vector<PropertyChangedCallback> property_changed_callbacks;
372 std::vector<FakeMethodAdder> fake_method_adders;
383 std::vector<std::string> used_attribute_name_list;
388 std::vector<std::string> used_static_attribute_name_list;
402 v8::Isolate * isolate;
423 v8::Global<v8::FunctionTemplate> global_parent_function_template;
429 std::vector<v8::Global<v8::FunctionTemplate>> this_class_function_templates;
437 bool finalized =
false;
444 bool wrap_as_most_derived_flag =
false;
447 std::list<MethodAdderData> method_adders;
453 v8::IndexedPropertyGetterCallback indexed_property_getter =
nullptr;
455 std::vector<FunctionTemplateCallback> function_template_callbacks;
501 void check_if_static_name_used(
const std::string & name);
508 void check_if_constructor_name_used(
std::string const &);
513 template<auto member>
515 v8::PropertyCallbackInfo<v8::Value>
const & info) {
517 auto isolate = info.GetIsolate();
522 info.GetReturnValue().Set(
CastToJS<std::add_lvalue_reference_t<decltype(cpp_object->*member)>>()(isolate, cpp_object->*member));
532 template<
auto member>
535 v8::PropertyCallbackInfo<void>
const & info) {
538 auto isolate = info.GetIsolate();
543 T * cpp_object = wrapper.get_cpp_object(info.Holder());
544 using MemberT = std::remove_reference_t<decltype(cpp_object->*member)>;
546 std::is_copy_assignable_v<MemberT> ||
547 std::is_move_assignable_v<MemberT>,
"Cannot add_member with a type that is not either copy or move assignable. Use add_member_readonly instead");
550 if constexpr(is_wrapped_type_v<MemberT>)
552 if constexpr(std::is_copy_assignable_v<MemberT>)
558 else if constexpr(std::is_move_assignable_v<MemberT>)
562 if (wrapper.does_object_own_memory(
object)) {
580 template<
typename DefaultArgsTupleType,
typename ... CONSTRUCTOR_PARAMETER_TYPES>
581 static void v8_constructor(
const v8::FunctionCallbackInfo<v8::Value>& info) {
582 auto isolate = info.GetIsolate();
585 DefaultArgsTupleType * default_args_tuple_ptr =
588 T * new_cpp_object =
nullptr;
589 func::function<void(CONSTRUCTOR_PARAMETER_TYPES...)> constructor =
590 [&new_cpp_object](CONSTRUCTOR_PARAMETER_TYPES... info)->
void{new_cpp_object =
new T(std::forward<CONSTRUCTOR_PARAMETER_TYPES>(info)...);};
596 std::index_sequence_for<CONSTRUCTOR_PARAMETER_TYPES...>{},
597 DefaultArgsTupleType(*default_args_tuple_ptr));
599 }
catch(std::exception & e) {
600 isolate->ThrowException(v8::String::NewFromUtf8(isolate, e.what()));
608 initialize_new_js_object(isolate, info.This(), new_cpp_object, deleter);
611 info.GetReturnValue().Set(info.This());
616 static void callback_helper(
const v8::FunctionCallbackInfo<v8::Value>& args);
622 std::unique_ptr<DestructorBehavior> destructor_behavior_delete = std::make_unique<DestructorBehavior_Delete<T>>();
623 std::unique_ptr<DestructorBehavior> destructor_behavior_leave_alone = std::make_unique<DestructorBehavior_LeaveAlone>();
651 if (!this->does_object_own_memory(
object)) {
652 throw CastException(
"Cannot V8ClassWrapper::release_internal_field_memory because the object does not own its own memory");
655 if (!std::has_virtual_destructor<T>::value &&
dynamic_cast<AnyPtr<T> *
>(wrapped_data->native_object) ==
nullptr) {
657 std::cerr << fmt::format(
"cached anybase type: {} vs T: {}", wrapped_data->native_object->type_name, demangle<T>()) << std::endl;
659 throw CastException(
"Tried to release internal field memory on the wrong type for a non-virtual-destructor type");
662 wrapped_data->weak_callback_data->global.ClearWeak();
665 delete wrapped_data->weak_callback_data;
666 wrapped_data->weak_callback_data =
nullptr;
675 if (wrapped_data->weak_callback_data) {
678 return wrapped_data->weak_callback_data !=
nullptr && wrapped_data->weak_callback_data->destructive;
687 #ifdef V8_CLASS_WRAPPER_DEBUG 688 fprintf(stderr,
"Initializing new js object for %s for v8::object at %p and cpp object at %p and any_ptr at %p\n", demangle<T>().c_str(), *js_object, cpp_object, (
void*)any_ptr);
690 if (js_object->InternalFieldCount() == 0) {
691 fprintf(stderr,
"Maybe you are calling a constructor without 'new'?");
693 assert(js_object->InternalFieldCount() >= 1);
699 [isolate, cpp_object, &destructor_behavior](v8::WeakCallbackInfo<SetWeakCallbackData>
const & info) {
700 destructor_behavior(isolate, cpp_object);
708 #ifdef V8_CLASS_WRAPPER_DEBUG 709 fprintf(stderr,
"inserting anyptr<%s>at address %p pointing to cpp object at %p\n",
typeid(T).name(), wrapped_data->native_object, cpp_object);
713 js_object->SetInternalField(0, v8::External::New(isolate, wrapped_data));
716 isolate->AdjustAmountOfExternalAllocatedMemory(
sizeof(T));
756 isolate_to_wrapper_map.erase(isolate);
759 used_constructor_name_list_map.erase(isolate);
769 auto wrapper_find_result = isolate_to_wrapper_map.find(isolate);
770 if ( wrapper_find_result != isolate_to_wrapper_map.end()) {
797 template<
class... CompatibleTypes>
798 std::enable_if_t<static_all_of<std::is_base_of<T,CompatibleTypes>::value...>::value>
802 assert(!is_finalized());
804 if (!std::is_const<T>::value) {
805 using ConstT = std::add_const_t<T>;
810 if (type_checker !=
nullptr) {
816 if (this->wrap_as_most_derived_object !=
nullptr) {
817 delete this->wrap_as_most_derived_object;
824 template<
template<
class>
class Deleter>
826 assert(!this->finalized);
827 using ConstT = std::add_const_t<T>;
828 if (!std::is_const<T>::value) {
831 this->destructor_behavior_delete = std::make_unique<Deleter<T>>();
841 template<
class ParentType>
842 std::enable_if_t<std::is_base_of<ParentType, T>::value>
844 assert(!is_finalized());
845 if (!std::is_const<T>::value) {
846 using ConstT = std::add_const_t<T>;
847 using ConstParent = std::add_const_t<ParentType>;
854 fprintf(stderr,
"Tried to set parent type of %s to unfinalized %s\n",
855 demangle<T>().c_str(), demangle<ParentType>().c_str());
861 global_parent_function_template =
862 v8::Global<v8::FunctionTemplate>(isolate,
879 template<
typename ... CONSTRUCTOR_PARAMETER_TYPES,
class DefaultArgsTuple = std::tuple<>>
882 DefaultArgsTuple
const & default_args = DefaultArgsTuple())
884 assert(((
void)
"Type must be finalized before calling add_constructor", this->finalized) ==
true);
885 check_if_constructor_name_used(js_constructor_name);
887 auto constructor_template =
889 v8::External::New(this->isolate,
new DefaultArgsTuple(
std::move(default_args))));
893 parent_template->Set(v8::String::NewFromUtf8(isolate, js_constructor_name.c_str()), constructor_template);
903 assert(((
void)
"Type must be finalized before calling expose_static_methods", this->finalized) ==
true);
904 check_if_constructor_name_used(js_name);
907 throw V8Exception(this->isolate,
"name conflicts with global names (bug: this ignores if your parent template isn't the global object)");
910 auto non_constructor_template =
911 make_wrapping_function_template([](
const v8::FunctionCallbackInfo<v8::Value>& args)->
void{
912 throw V8Exception(args.GetIsolate(),
"You cannot create an object of this type");
918 parent_template->Set(v8::String::NewFromUtf8(isolate, js_name.c_str()), non_constructor_template);
932 auto isolate = this->isolate;
933 assert(existing_cpp_object !=
nullptr);
937 if (!this->is_finalized()) {
940 throw CastException(fmt::format(
"Tried to wrap existing cpp object for a type that isn't finalized: {}", demangle<T>()));
943 #ifdef V8_CLASS_WRAPPER_DEBUG 944 fprintf(stderr,
"Wrapping existing c++ object %p in v8 wrapper this: %p isolate %p type: %s\n", existing_cpp_object,
this, isolate, v8toolkit::demangle<T>().c_str());
950 if(this->existing_wrapped_objects.find(existing_cpp_object) != this->existing_wrapped_objects.end()) {
951 #ifdef V8_CLASS_WRAPPER_DEBUG 952 fprintf(stderr,
"Found existing javascript object for c++ object %p - %s\n", existing_cpp_object, v8toolkit::demangle<T>().c_str());
958 V8TOOLKIT_DEBUG(
"Creating new javascript object for c++ object %p - %s\n", existing_cpp_object, v8toolkit::demangle<T>().c_str());
960 v8::Isolate::Scope is(isolate);
961 v8::Context::Scope cs(context);
963 #ifdef V8TOOLKIT_BIDIRECTIONAL_ENABLED 966 if (!jswrapper_javascript_object.IsEmpty()) {
967 return jswrapper_javascript_object;
972 if (this->wrap_as_most_derived_flag && !force_wrap_this_type) {
973 javascript_object = this->wrap_as_most_derived(existing_cpp_object, destructor_behavior);
975 javascript_object = get_function_template()->GetFunction()->NewInstance();
980 initialize_new_js_object(isolate, javascript_object, existing_cpp_object, destructor_behavior);
982 this->existing_wrapped_objects.emplace(existing_cpp_object,
983 v8::Global<v8::Object>(isolate, javascript_object));
985 V8TOOLKIT_DEBUG(
"Inserting new %s object into existing_wrapped_objects hash that is now of size: %d\n",
typeid(T).name(), (
int)this->existing_wrapped_objects.size());
986 V8TOOLKIT_DEBUG(
"Wrap existing cpp object returning object about to be cast to a value: %s - %s\n", *v8::String::Utf8Value(javascript_object), v8toolkit::demangle<T>().c_str());
989 return javascript_object;
995 template<
class R,
class... Params,
class DefaultArgs = std::tuple<>>
997 static std::vector<std::string> reserved_names = {
"arguments",
"arity",
"caller",
"displayName",
998 "length",
"name",
"prototype"};
1000 if (std::find(reserved_names.begin(), reserved_names.end(), method_name) != reserved_names.end()) {
1001 throw InvalidCallException(fmt::format(
"The name: '{}' is a reserved property in javascript functions, so it cannot be used as a static method name", method_name));
1004 if (!std::is_const<T>::value) {
1009 assert(!this->finalized);
1011 this->check_if_static_name_used(method_name);
1017 callable, method_name);
1019 constructor_function_template->Set(this->isolate,
1020 method_name.c_str(),
1021 static_method_function_template);
1024 this->static_method_adders.emplace_back(static_method_adder);
1029 template<
class Callable,
class DefaultArgs = std::tuple<>>
1031 if (!std::is_const<T>::value) {
1036 assert(!this->finalized);
1038 this->check_if_static_name_used(method_name);
1043 callable, method_name);
1046 constructor_function_template->Set(this->isolate,
1047 method_name.c_str(),
1048 static_method_function_template);
1051 this->static_method_adders.emplace_back(static_method_adder);
1063 void finalize(
bool wrap_as_most_derived =
false);
1069 return this->finalized;
1078 template<
class MemberType,
1080 MemberType (MemberClass::*member)>
1082 this->add_member<member>(member_name);
1086 template<auto member>
1088 assert(this->finalized ==
false);
1094 static_assert(!is_pointer_to_const_data_member_v<member>,
"Cannot V8ClassWrapper::add_member a const data member. Use add_member_readonly instead");
1096 if constexpr(!std::is_const_v<T>) {
1098 template add_member_readonly<member>(member_name);
1101 this->check_if_name_used(member_name);
1108 _getter_helper<member>,
1109 _setter_helper<member>);
1114 template<
class MemberType,
1116 MemberType (MemberClass::*member),
1117 std::enable_if_t<std::is_base_of<MemberClass, T>::value,
int> = 0>
1119 this->add_member_readonly<member>(member_name);
1122 template<auto member>
1126 if (!std::is_const<T>::value) {
1130 assert(this->finalized ==
false);
1132 this->check_if_name_used(member_name);
1136 constructor_template->SetAccessor(v8::String::NewFromUtf8(isolate, member_name.c_str()),
1137 _getter_helper<member>,
1147 template<
class R,
class TBase,
class...
Args,
1148 std::enable_if_t<std::is_base_of<TBase, T>::value,
int> = 0>
1162 template<
class R,
class TBase,
class...
Args,
class DefaultArgs = std::tuple<>,
1163 std::enable_if_t<std::is_base_of<TBase, T>::value,
int> = 0>
1165 if (!std::is_const<T>::value) {
1177 template<
class R,
class TBase,
class...
Args,
class DefaultArgs = std::tuple<>,
1178 std::enable_if_t<std::is_base_of<TBase, T>::value,
int> = 0>
1180 if (!std::is_const<T>::value) {
1193 template<
class R,
class TBase,
class...
Args,
class DefaultArgs = std::tuple<>,
1194 std::enable_if_t<std::is_base_of<TBase, T>::value,
int> = 0>
1196 static_assert(std::is_same<R, void>::value && !std::is_same<R, void>::value,
"not supported");
1206 template<
class R,
class TBase,
class...
Args,
class DefaultArgs = std::tuple<>,
1207 std::enable_if_t<std::is_base_of<TBase, T>::value,
int> = 0>
1219 template<
class R,
class TBase,
class...
Args,
class DefaultArgs = std::tuple<>,
1220 std::enable_if_t<std::is_base_of<TBase, T>::value,
int> = 0>
1233 template<
class R,
class TBase,
class...
Args,
class DefaultArgs = std::tuple<>,
1234 std::enable_if_t<std::is_base_of<TBase, T>::value,
int> = 0>
1237 static_assert(std::is_same<R, void>::value && !std::is_same<R, void>::value,
"not supported");
1246 template<
class R,
class Head,
class... Tail,
class DefaultArgs = std::tuple<>,
1247 std::enable_if_t<std::is_const<Head>::value && !std::is_const<T>::value,
int> = 0>
1249 DefaultArgs
const & default_args = DefaultArgs()) {
1257 template<
class R,
class Head,
class... Tail,
class DefaultArgs = std::tuple<>,
1258 std::enable_if_t<!(std::is_const<Head>::value && !std::is_const<T>::value),
int> = 0>
1260 DefaultArgs
const & default_args = DefaultArgs()) {
1271 template<
class R,
class Head,
class...
Args,
class DefaultArgs = std::tuple<>,
1272 std::enable_if_t<std::is_pointer<Head>::value &&
1273 std::is_same<std::remove_const_t<std::remove_pointer_t<Head>>, T>::value,
int> = 0>
1275 DefaultArgs
const & default_args = DefaultArgs()) {
1276 _add_fake_method(method_name, method, default_args);
1286 template<
class R,
class Head,
class...
Args,
class DefaultArgs = std::tuple<>,
1287 std::enable_if_t<std::is_pointer<Head>::value &&
1288 std::is_base_of<std::remove_const_t<std::remove_pointer_t<Head>>, T>::value,
int> = 0>
1290 DefaultArgs
const & default_args = DefaultArgs()) {
1292 _add_fake_method(method_name,
func::function<R(Head,
Args...)>(method), default_args);
1299 template<
class Callback,
class DefaultArgs = std::tuple<> >
1301 DefaultArgs
const & default_args = DefaultArgs()) {
1303 this->_add_fake_method(method_name, f, default_args);
1309 return this->wrap_as_most_derived_object->operator()(cpp_object, destructor_behavior);
1314 template<
class R,
class Head,
class... Tail,
class DefaultArgsTuple,
1315 std::enable_if_t<std::is_pointer<Head>::value &&
1316 std::is_same<std::remove_const_t<std::remove_pointer_t<Head>>, std::remove_const_t<T>>::value,
int> = 0>
1319 assert(this->finalized ==
false);
1322 add_fake_method_for_const_type(method_name, method);
1324 this->check_if_name_used(method_name);
1332 CopyFunctionType * copy =
new func::function<R(Head, Tail...)>(method);
1337 new StdFunctionCallbackType([method_name, default_args, copy](
const v8::FunctionCallbackInfo<v8::Value>& info) {
1340 auto fake_method = *(
func::function<R(Head, Tail...)>*)v8::External::Cast(*(info.Data()))->Value();
1341 auto isolate = info.GetIsolate();
1343 auto holder = info.Holder();
1346 assert(holder->InternalFieldCount() == 1);
1356 }
catch(std::exception & e) {
1357 isolate->ThrowException(v8::String::NewFromUtf8(isolate, e.what()));
1364 auto function_template = v8::FunctionTemplate::New(this->isolate);
1365 function_template->SetCallHandler(callback_helper, v8::External::New(this->isolate, method_caller));
1368 prototype_template->Set(v8::String::NewFromUtf8(isolate, method_name.c_str()), function_template);
1379 template<
class M,
class...
Args,
class... DefaultArgTypes>
1383 std::tuple<DefaultArgTypes...>
const & default_args_tuple,
1384 bool add_as_callable_object_callback =
false)
1387 assert(this->finalized ==
false);
1389 this->check_if_name_used(method_name);
1392 StdFunctionCallbackType([
this, default_args_tuple, method, method_name](
const v8::FunctionCallbackInfo<v8::Value>& info) {
1393 auto isolate = info.GetIsolate();
1398 auto holder = info.Holder();
1401 #ifdef V8_CLASS_WRAPPER_DEBUG 1402 fprintf(stderr,
"Looking for instance match in prototype chain %s :: %s\n", demangle<T>().c_str(),
1403 demangle<M>().c_str());
1404 fprintf(stderr,
"Holder: %s\n",
stringify_value(isolate, holder).c_str());
1408 auto function_template_count = this->this_class_function_templates.size();
1409 int current_template_count = 0;
1410 for (
auto &function_template : this->this_class_function_templates) {
1411 current_template_count++;
1412 V8TOOLKIT_DEBUG(
"Checking function template %d / %d\n", current_template_count,
1413 (
int) function_template_count);
1414 self = holder->FindInstanceInPrototypeChain(function_template.Get(isolate));
1415 if (!
self.IsEmpty() && !
self->IsNull()) {
1422 if (
self.IsEmpty()) {
1423 V8TOOLKIT_DEBUG(
"No match in prototype chain after looking through all potential function templates\n");
1434 wrapped_data->native_object);
1439 auto bound_method = v8toolkit::bind<T>(*backing_object_pointer, method);
1448 }
catch (std::exception &e) {
1449 isolate->ThrowException(v8::String::NewFromUtf8(isolate, e.what()));
1455 if (add_as_callable_object_callback) {
1458 callable_adder = method_adder_data;
1460 method_adders.push_back(method_adder_data);
1472 assert(indexed_property_getter ==
nullptr);
1473 indexed_property_getter = getter;
1477 struct NamedPropertyCallbackData {
1478 T * cpp_object =
nullptr;
1480 v8::PropertyCallbackInfo<v8::Value>
const &)> getter;
1483 v8::PropertyCallbackInfo<v8::Value>
const &)> setter;
1486 template<
class ReturnT>
1489 template<
class ReturnT>
1495 class NoResultAvailable :
public std::exception {
1497 virtual const char *
what() const noexcept
override {
return "";}
1504 template<
class ResultT>
1506 v8::PropertyCallbackInfo<v8::Value>
const &info) {
1507 if (result !=
nullptr) {
1508 info.GetReturnValue().Set(
CastToJS<ResultT>()(info.GetIsolate(), std::forward<ResultT>(result)));
1513 template<
class ResultT>
1515 v8::PropertyCallbackInfo<v8::Value>
const &info) {
1527 template<
class GetterReturnT,
class SetterReturnT>
1530 assert(!this->finalized);
1531 assert(!this->named_property_adder);
1534 auto data =
new NamedPropertyCallbackData();
1536 if (getter_callback) {
1538 v8::PropertyCallbackInfo<v8::Value>
const &info) {
1540 if (property_name->IsSymbol()) {
1547 auto str = *v8::String::Utf8Value(property_name);
1549 handle_getter_result(getter_callback(cpp_object, str), info);
1551 }
catch(NoResultAvailable) {
1556 if (setter_callback) {
1559 v8::PropertyCallbackInfo<v8::Value>
const &info) {
1562 setter_callback(cpp_object, *v8::String::Utf8Value(property_name)) =
1564 new_property_value);
1565 info.GetReturnValue().Set(
true);
1570 object_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
1573 v8::PropertyCallbackInfo<v8::Value>
const & info){
1574 auto external_data = v8::External::Cast(*info.Data());
1575 NamedPropertyCallbackData * data =
static_cast<NamedPropertyCallbackData *
>(external_data->Value());
1576 data->getter(property_name, info);
1581 v8::PropertyCallbackInfo<v8::Value>
const & info){
1582 auto external_data = v8::External::Cast(*info.Data());
1583 NamedPropertyCallbackData * data =
static_cast<NamedPropertyCallbackData *
>(external_data->Value());
1584 data->setter(property_name, new_property_value, info);
1589 printf(
"In query callback %s\n", *v8::String::Utf8Value(property_name));
1593 v8::PropertyCallbackInfo<v8::Boolean>
const & info){
1594 printf(
"IN DELETER CALLBACK %s\n", *v8::String::Utf8Value(property_name));
1597 [](v8::PropertyCallbackInfo<v8::Array>
const & info) {
1598 printf(
"IN ENUMERATOR CALLBACK\n");
1599 info.GetReturnValue().Set(v8::Array::New(info.GetIsolate()));
1601 v8::External::New(this->isolate, (
void *)data),
1602 v8::PropertyHandlerFlags::kNonMasking
1613 template<
class GetterReturnT,
class GetterObjectT,
class SetterReturnT,
class SetterObjectT>
1614 void add_named_property_handler(GetterReturnT(GetterObjectT::*getter_callback)(
std::string const &)
const,
1615 SetterReturnT(SetterObjectT::*setter_callback)(
std::string const &)) {
1618 if (getter_callback !=
nullptr) {
1619 bound_getter =
std::bind(getter_callback, std::placeholders::_1, std::placeholders::_2);
1621 if (setter_callback !=
nullptr) {
1622 bound_setter =
std::bind(setter_callback, std::placeholders::_1, std::placeholders::_2);
1625 this->add_named_property_handler(bound_getter, bound_setter);
1635 assert(!this->finalized);
1636 function_template_callbacks.push_back(callback);
1643 #ifndef V8TOOLKIT_WRAPPER_FAST_COMPILE 1667 return wrapper.wrap_existing_cpp_object(isolate->GetCurrentContext(), &cpp_object,
1668 *wrapper.destructor_behavior_leave_alone);
1674 return wrapper.wrap_existing_cpp_object(isolate->GetCurrentContext(),
new T(
std::move(cpp_object)),
1675 *wrapper.destructor_behavior_delete);
1683 template<
class T,
class... Rest>
1684 struct CastToJS<
std::unique_ptr<T, Rest...>, std::enable_if_t<is_wrapped_type_v<T>>> {
1698 auto result = wrapper.wrap_existing_cpp_object(isolate->GetCurrentContext(), unique_ptr.release(), *wrapper.destructor_behavior_delete);
1713 struct CastToJS<T *, std::enable_if_t<is_wrapped_type_v<T>>> {
1717 if (cpp_object ==
nullptr) {
1718 return v8::Undefined(isolate);
1721 assert(cpp_object != (
void *) 0xbebebebebebebebe);
1724 auto context = isolate->GetCurrentContext();
1727 #ifdef V8TOOLKIT_BIDIRECTIONAL_ENABLED 1731 if (std::is_const<T>::value) {
1738 using NonConstT = std::remove_const_t<T>;
1739 auto js_wrapper =
safe_dynamic_cast<JSWrapperType *>(
const_cast<NonConstT *
>(cpp_object));
1746 V8TOOLKIT_DEBUG(
"CastToJS<T*> returning wrapped existing object for %s\n",
typeid(T).name());
1749 return class_wrapper.template wrap_existing_cpp_object(context, cpp_object,
1750 *class_wrapper.destructor_behavior_leave_alone);
1756 struct CastToJS<T&, std::enable_if_t<is_wrapped_type_v<T>>> {
1766 struct CastToJS<T&&, std::enable_if_t<is_wrapped_type_v<T>>> {
1776 template<
typename T>
1777 struct CastToNative<T,
std::enable_if_t<!std::is_const_v<T> && std::is_copy_constructible<T>::value && is_wrapped_type_v<T>>>
1787 template<
typename T>
1788 struct CastToNative<T,
std::enable_if_t<!std::is_copy_constructible<T>::value && is_wrapped_type_v<T>>>
1790 template<
class U = T>
1792 static_assert(always_false_v<T>,
"Cannot return a copy of an object of a type that is not copy constructible");
1800 template<
typename T>
1810 template<
typename T>
1822 throw CastException(
"Could not cast object to {} && because it doesn't own it's memory", demangle<T>());
1827 template<
typename T>
1834 return wrapper.get_cpp_object(
object);
1843 return fmt::format(
"const: {} pointer: {} reference: {} typeid: {}",
1844 std::is_const<T>::value, std::is_pointer<T>::value,
1845 std::is_reference<T>::value,
typeid(T).name());
1850 template<class T, std::enable_if_t<std::is_pointer<T>::value || std::is_reference<T>::value,
int> = 0>
1852 throw CastException(fmt::format(
"Pointer and reference types ({}) won't ever succeed in getting an embedded cpp object", demangle<T>()));
1858 template<class T, std::enable_if_t<!std::is_pointer<T>::value && !std::is_reference<T>::value,
int> = 0>
1864 if (object->InternalFieldCount() <= 0) {
1865 throw CastException(fmt::format(
"No specialization CastToNative<{}> found (for any shortcut notation) and provided Object is not a wrapped C++ object. It is a native Javascript Object", demangle<T>()));
1868 auto wrapped_data =
static_cast<WrappedData<T> *
>(wrap->Value());
1873 fprintf(stderr,
"Failed to convert types: want: %d %s\n", std::is_const<T>::value,
typeid(T).name());
1874 throw CastException(fmt::format(
"Cannot convert AnyBase to {}", demangle<T>()));
1880 template<
class T,
class... Rest>
1891 return std::unique_ptr<T, Rest...>(wrapper.release_internal_field_memory(
object));
1892 }
else if constexpr(std::is_copy_constructible_v<T>) {
1893 T & cpp_object = get_object_from_embedded_cpp_object<T>(isolate, value);
1894 return std::unique_ptr<T, Rest...>(
new T(cpp_object));
1896 throw CastException(
"Cannot CastToNative<unique_ptr<{}>> from object that doesn't own it's memory and isn't copy constructible: {}",
1897 demangle<T>(), *v8::String::Utf8Value(value));
1907 auto context = this->isolate->GetCurrentContext();
1911 return wrapper.template wrap_existing_cpp_object(context, cpp_object, destructor_behavior,
true );
1916 template<
class T,
class Head,
class... Tail> T *
1918 std::enable_if_t<std::is_const<T>::value ||
1919 !std::is_const<Head>::value>
1920 >::check(
AnyBase * any_base,
bool first_call)
const {
1921 assert(any_base !=
nullptr);
1922 ANYBASE_PRINT(
"typechecker::check for {} with anyptr {} (string: {})", demangle<Head>(), (
void*)any_base, any_base->
type_name);
1923 if(
auto any =
dynamic_cast<AnyPtr<Head> *
>(any_base)) {
1924 ANYBASE_PRINT(
"Got match on: {}, returning {}", demangle<Head>(), (
void*)(any->get()));
1925 return static_cast<T*
>(any->get());
1928 ANYBASE_PRINT(
"didn't find match, testing const type now...");
1931 if (!std::is_same<std::remove_const_t<T>, std::remove_const_t<Head>>::value) {
1933 return derived_result;
1937 ANYBASE_PRINT(
"no match on const type either, continuing down chain");
1939 return SUPER::check(any_base,
false);
1946 template<
class T,
class Head,
class... Tail>
1948 std::enable_if_t<!std::is_const<T>::value || std::is_const<Head>::value>>
1952 if (!std::is_same<std::remove_const_t<T>, std::remove_const_t<Head>>::value) {
1953 using MatchingConstT = std::conditional_t<std::is_const<Head>::value, std::add_const_t<T>, std::remove_const_t<T>>;
1955 if (std::is_const<T>::value == std::is_const<Head>::value) {
1956 if (
auto derived = safe_dynamic_cast<Head *>(const_cast<MatchingConstT *>(cpp_object))) {
1961 return SUPER::operator()(cpp_object, destructor_behavior);
1967 struct ParameterBuilder<T,
std::enable_if_t<std::is_reference_v<T> && is_wrapped_type_v<std::remove_reference_t<T>>> > {
1972 template<
int default_arg_position = -1,
class DefaultArgsTuple = std::tuple<>>
1973 T
operator()(
const v8::FunctionCallbackInfo<v8::Value> & info,
int & i,
1974 std::vector<std::unique_ptr<StuffBase>> & stuff,
1975 DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
1976 PB_PRINT(
"ParameterBuilder handling wrapped type: {} {}", demangle<T>(),
1977 std::is_rvalue_reference_v<T> ?
"&&" :
"&");
1978 auto isolate = info.GetIsolate();
1980 if (i >= info.Length()) {
1981 return std::forward<T>(*get_default_parameter<NoConstRefT, default_arg_position>(info, i, stuff, default_args_tuple));
1985 auto value = info[i++];
1987 if (value->IsObject()) {
1988 auto object = value->ToObject();
1989 if (
auto cpp_object = wrapper.get_instance(isolate).get_cpp_object(
object)) {
1991 if constexpr(std::is_rvalue_reference_v<T>)
1993 if (wrapper.does_object_own_memory(
object)) {
1995 }
else if constexpr(std::is_copy_constructible_v<NoConstRefT>)
2014 throw CastException(
"Could not create requested object of type: {} {}. Maybe you don't 'own' your memory?",
2016 std::is_rvalue_reference_v<T> ?
"&&" :
"&");
2028 template<
int default_arg_position = -1,
class DefaultArgsTuple = std::tuple<>>
2029 T
operator()(
const v8::FunctionCallbackInfo<v8::Value> & info,
int & i,
2030 std::vector<std::unique_ptr<StuffBase>> & stuff,
2031 DefaultArgsTuple && default_args_tuple = DefaultArgsTuple()) {
2032 PB_PRINT(
"ParameterBuilder handling wrapped type: {}", demangle<T>());
2033 auto isolate = info.GetIsolate();
2036 if (i >= info.Length()) {
2037 return *get_default_parameter<T, default_arg_position>(info, i, stuff, default_args_tuple);
2039 auto value = info[i++];
2040 if (value->IsObject()) {
2041 auto object = value->ToObject();
2042 if (
auto cpp_object = wrapper.get_instance(isolate).get_cpp_object(
object)) {
2325 template<
typename T>
2326 struct CastToJS<T*, std::enable_if_t<std::is_polymorphic<T>::value>> {
2328 if (cpp_object ==
nullptr) {
2329 return v8::Undefined(isolate);
2332 assert(cpp_object != (
void *)0xbebebebebebebebe);
2335 auto context = isolate->GetCurrentContext();
2338 #ifdef V8TOOLKIT_BIDIRECTIONAL_ENABLED 2340 using JSWrapperType = JSWrapp er<std::remove_const_t<T>>;
2343 if (std::is_const<T>::value) {
2344 auto js_wrapper =
dynamic_cast<JSWrapperType
const *
>(cpp_object);
2350 using NonConstT = std::remove_const_t<T>;
2351 auto js_wrapper =
dynamic_cast<JSWrapperType *
>(
const_cast<NonConstT *
>(cpp_object));
2358 V8TOOLKIT_DEBUG(
"CastToJS<T*> returning wrapped existing object for %s\n",
typeid(T).name());
2441 v8::Local<v8::Value> operator()(v8::Isolate * isolate, std::remove_reference_t<T> && cpp_object){
2442 using NoRefT = std::remove_reference_t<T>;
2443 V8TOOLKIT_DEBUG("In base cast to js struct with rvalue ref");
2444 V8TOOLKIT_DEBUG("Asked to convert rvalue type, so copying it first\n");
2446 // this memory will be owned by the javascript object and cleaned up if/when the GC removes the object
2447 auto copy = new NoRefT(std::move(cpp_object));
2448 auto context = isolate->GetCurrentContext();
2449 V8ClassWrapper<NoRefT> & class_wrapper = V8ClassWrapper<NoRefT>::get_instance(isolate);
2450 auto result = class_wrapper.template wrap_existing_cpp_object(context, copy, *class_wrapper.destructor_behavior_delete);
2451 V8TOOLKIT_DEBUG("CastToJS<T> returning wrapped existing object: %s\n", *v8::String::Utf8Value(result));
2458 template<typename T>
2459 struct CastToJS<T*&> {
2460 v8::Local<v8::Value> operator()(v8::Isolate * isolate, T * cpp_object) {
2461 return CastToJS<T*>()(isolate, cpp_object);
2466 template<typename T>
2467 struct CastToJS<T const *&> {
2468 v8::Local<v8::Value> operator()(v8::Isolate * isolate, T const * cpp_object) {
2469 return CastToJS<T const *>()(isolate, cpp_object);
2474 template<typename T>
2475 struct CastToJS<T* const &> {
2476 v8::Local<v8::Value> operator()(v8::Isolate * isolate, T * const cpp_object) {
2477 return CastToJS<T * const>()(isolate, cpp_object);
2482 template<typename T>
2483 struct CastToJS<T const * const &> {
2484 v8::Local<v8::Value> operator()(v8::Isolate * isolate, T const * const cpp_object) {
2485 return CastToJS<T const * const>()(isolate, cpp_object);
#define ANYBASE_PRINT(format_string,...)
#define V8TOOLKIT_V8CLASSWRAPPER_USE_REAL_TEMPLATE_SFINAE
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
bool Value(const T &value, M matcher)
#define V8TOOLKIT_DEBUG(format_string,...)
#define PB_PRINT(format_string,...)
eastl::vector_map< ArgTs... > MapT
const T & move(const T &t)
#define HANDLE_FUNCTION_VALUES