#ifndef EFL_EINA_JS_COMPATIBILITY_HH #define EFL_EINA_JS_COMPATIBILITY_HH #include #include #include #include #include #ifdef HAVE_NODE_V8_H #include #elif defined(HAVE_NODEJS_DEPS_V8_V8_H) #include #elif defined(HAVE_NODEJS_DEPS_V8_INCLUDE_V8_H) #include #elif defined(HAVE_NODEJS_SRC_V8_H) #include #elif defined(HAVE_V8_H) #include #else #error We must have at least one v8 header to include #endif namespace v8 { template struct FunctionCallbackInfo; template struct PropertyCallbackInfo; template #ifdef HAVE_V8_GLOBAL struct Global; #else struct UniquePersistent; #endif class AccessorInfo; class Arguments; } namespace efl { namespace eina { namespace js { template struct value_tag { typedef T type; }; template struct complex_tag { T value; typedef std::tuple inner_types; }; template struct make_tag_traits { typedef typename std::remove_reference::type a1; typedef typename std::conditional ::value , typename std::remove_cv::type , T>::type type; }; template using make_complex_tag = complex_tag::type , typename make_tag_traits::type...>; template struct struct_tag { T value; }; template using make_struct_tag = struct_tag::type>; template struct struct_ptr_tag { T value; }; template using make_struct_ptr_tag = struct_ptr_tag::type>; template struct remove_tag { typedef T type; }; template struct remove_tag> { typedef typename eina::js::remove_tag::type type; }; template struct remove_tag> { typedef typename eina::js::remove_tag::type type; }; template struct remove_tag> { typedef typename eina::js::remove_tag::type type; }; template struct is_handable_by_value { static constexpr bool value = !std::is_class::value || std::is_same::value; }; template struct container_wrapper { typedef typename eina::js::remove_tag::type _notag_type; typedef typename std::conditional< std::is_convertible<_notag_type, Eo const* const>::value , typename std::conditional< std::is_const::type>::value , ::efl::eo::concrete const , ::efl::eo::concrete >::type , _notag_type >::type type; }; template T container_wrap(T&& v) { return std::forward(v); } inline ::efl::eo::concrete container_wrap(Eo* v) { if(v) eo_ref(v); return ::efl::eo::concrete{v}; } inline ::efl::eo::concrete container_wrap(Eo const* v) { if (v) eo_ref(v); return ::efl::eo::concrete{const_cast(v)}; } template T& container_unwrap(T& v) { return v; } inline Eo* container_unwrap(::efl::eo::concrete& v) { return v._eo_ptr(); } inline Eo* container_unwrap(::efl::eo::concrete const& v) { return v._eo_ptr(); } template struct is_complex_tag : std::false_type {}; template struct is_complex_tag> : std::true_type {}; template struct is_struct_tag : std::false_type {}; template struct is_struct_tag> : std::true_type {}; template struct is_struct_ptr_tag : std::false_type {}; template struct is_struct_ptr_tag> : std::true_type {}; template struct is_type_tag { static constexpr bool value = is_complex_tag::value || is_struct_tag::value || is_struct_ptr_tag::value; }; // Class name getters struct cls_name_getter_base {}; struct cls_name_getter_generated_base : cls_name_getter_base {}; /// Name getter for types that are not classes struct nonclass_cls_name_getter : cls_name_getter_base { static char const* class_name() { return ""; } }; // JS container base enum container_type { list_container_type , array_container_type , container_type_size }; struct eina_container_base { virtual ~eina_container_base() {} virtual std::size_t size() const = 0; virtual eina_container_base* concat(eina_container_base const& rhs) const = 0; virtual eina_container_base* slice(std::int64_t i, std::int64_t j) const = 0; virtual int index_of(v8::Isolate* isolate, v8::Local v) const = 0; virtual int last_index_of(v8::Isolate* isolate, v8::Local v) const = 0; virtual v8::Local get(v8::Isolate*, std::size_t) const = 0; virtual v8::Local set(v8::Isolate* isolate, std::size_t index, v8::Local v) = 0; virtual int push(v8::Isolate* isolate, v8::Local v) = 0; virtual v8::Local pop(v8::Isolate* isolate) = 0; virtual v8::Local to_string(v8::Isolate*) const = 0; virtual v8::Local join(v8::Isolate*, v8::Local separator) const = 0; virtual container_type get_container_type() const = 0; virtual void* get_container_native_handle() = 0; virtual void const* get_container_native_handle() const = 0; }; // Containers forward declarations // Array template ::type> struct eina_array; template ::type> struct range_eina_array; EAPI v8::Handle get_array_instance_template(); // List template ::type> struct eina_list; template ::type> struct range_eina_list; EAPI v8::Handle get_list_instance_template(); // Accessor template v8::Local export_accessor(::efl::eina::accessor&, v8::Isolate*, const char*); template ::efl::eina::accessor& import_accessor(v8::Handle); // Wrap value functions template typename std::remove_cv::type>::type wrap_value(T v, value_tag , typename std::enable_if::type>::value>::type* = 0) { return v; } template R wrap_value(T const& v, value_tag>) { return R {v}; } template R wrap_value(T v, value_tag>) { return R {v}; } template R wrap_value(T v, value_tag>) { return R {v}; } template struct _libv8_isolate_test { using new_signature = v8::Local(*)(v8::Isolate*, void*); static const bool value = std::is_same(&T::New)), new_signature>::value; }; template struct _libv8_callback_info_test; typedef v8::Handle(*_libv8_invocation_callback)(v8::Arguments const&); template struct _libv8_callback_info_test )>::value>::type> : std::true_type { }; template struct _libv8_callback_info_test )>::value>::type> : std::false_type { }; // NOTE: ifndef needed because a bug in doxygen makes it fail with this class #ifndef EFL_DOXYGEN template struct _libv8_property_callback_info_test : std::true_type {}; #endif typedef v8::Handle(*_libv8_getter_callback)(v8::Local, v8::AccessorInfo const&); typedef void(*_libv8_setter_callback)(v8::Local, v8::Local, v8::AccessorInfo const&); #ifndef EFL_DOXYGEN template struct _libv8_property_callback_info_test , _libv8_getter_callback , _libv8_setter_callback , v8::Handle , v8::AccessControl , v8::PropertyAttribute , v8::Handle )>::value>::type> : std::false_type { }; #endif static constexpr bool const v8_uses_isolate = _libv8_isolate_test<>::value; static constexpr bool const v8_uses_callback_info = _libv8_callback_info_test<>::value; static constexpr bool const v8_uses_property_callback_info = _libv8_property_callback_info_test<>::value; using compatibility_return_type = std::conditional >::type; using compatibility_callback_info_type = std::conditional const&, v8::Arguments const&> ::type; using compatibility_callback_info_pointer = std::conditional const*, v8::Arguments const*> ::type; typedef compatibility_return_type(*compatibility_function_callback)(compatibility_callback_info_type); using compatibility_accessor_getter_return_type = std::conditional >::type; using compatibility_accessor_getter_callback_info_type = std::conditional const&, v8::AccessorInfo const&> ::type; using compatibility_accessor_setter_return_type = void; using compatibility_accessor_setter_callback_info_type = std::conditional const&, v8::AccessorInfo const&> ::type; using compatibility_indexed_property_getset_return_type = std::conditional >::type; using compatibility_indexed_property_callback_info_type = std::conditional const&, v8::AccessorInfo const&> ::type; static_assert(v8_uses_property_callback_info == v8_uses_callback_info && v8_uses_callback_info == v8_uses_isolate, ""); template struct compatibility_type_tag {}; template struct compatibility_string; template <> struct compatibility_string : v8::String { template static v8::Local New(Args...args) { return NewFromUtf8(v8::Isolate::GetCurrent(), args...); } }; template <> struct compatibility_string : v8::String { }; template auto compatibility_new_impl(v8::Isolate*, std::true_type, compatibility_type_tag , Args...args) -> decltype(compatibility_string<>::New(args...)) { return compatibility_string<>::New(args...); } template auto compatibility_new_impl(v8::Isolate*, std::false_type, compatibility_type_tag , Args...args) -> decltype(compatibility_string<>::New(args...)) { return compatibility_string<>::New(args...); } template auto compatibility_new_impl(std::nullptr_t, std::true_type, compatibility_type_tag , Args...args) -> decltype(compatibility_string<>::New(args...)) { return compatibility_string<>::New(args...); } template auto compatibility_new_impl(std::nullptr_t, std::false_type, compatibility_type_tag , Args...args) -> decltype(compatibility_string<>::New(args...)) { return compatibility_string<>::New(args...); } template auto compatibility_new_impl(v8::Isolate* isolate, std::true_type, compatibility_type_tag , Args...args) -> decltype(T::New(isolate, args...)) { return T::New(isolate, args...); } template auto compatibility_new_impl(v8::Isolate*, std::false_type, compatibility_type_tag , Args...args) -> decltype(T::New(args...)) { return T::New(args...); } template auto compatibility_new_impl(std::nullptr_t, std::true_type, compatibility_type_tag , Args...args) -> decltype(T::New(v8::Isolate::GetCurrent(), args...)) { return T::New(v8::Isolate::GetCurrent(), args...); } template auto compatibility_new_impl(std::nullptr_t, std::false_type, compatibility_type_tag , Args...args) -> decltype(T::New(args...)) { return T::New(args...); } template auto compatibility_new(v8::Isolate* isolate, Args...args) -> decltype(js::compatibility_new_impl<> (isolate, std::integral_constant() , compatibility_type_tag() , args...)) { return js::compatibility_new_impl(isolate, std::integral_constant() , compatibility_type_tag() , args...); } template auto compatibility_new(std::nullptr_t, Args...args) -> decltype(js::compatibility_new_impl<>(nullptr, std::integral_constant() , compatibility_type_tag() , args...)) { return js::compatibility_new_impl<>(nullptr, std::integral_constant() , compatibility_type_tag() , args...); } #ifdef HAVE_V8_CREATE_PARAMS namespace detail { class array_buffer_allocator : public v8::ArrayBuffer::Allocator { public: virtual void* Allocate(std::size_t length) { void* data = AllocateUninitialized(length); return data ? std::memset(data, 0, length) : data; } virtual void* AllocateUninitialized(std::size_t length) { return std::malloc(length); } virtual void Free(void* data, std::size_t) { std::free(data); } }; } inline v8::Isolate* compatibility_isolate_new() { static detail::array_buffer_allocator allocator; v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = &allocator; return v8::Isolate::New(create_params); } #else inline v8::Isolate* compatibility_isolate_new() { return v8::Isolate::New(); } #endif template inline void compatibility_return_impl(T object, U const& info, std::true_type) { info.GetReturnValue().Set(object); } template inline v8::Handle compatibility_return_impl(T object, compatibility_callback_info_type, std::false_type) { return object; } template inline v8::Handle compatibility_return_impl(T object, compatibility_accessor_getter_callback_info_type, std::false_type) { return object; } template compatibility_return_type compatibility_return(T object, compatibility_callback_info_type args) { return compatibility_return_impl(object, args, std::integral_constant()); } template compatibility_return_type compatibility_return(T object, compatibility_accessor_getter_callback_info_type args) { return compatibility_return_impl(object, args, std::integral_constant()); } inline void compatibility_return_nil_impl(std::true_type) {} inline v8::Handle compatibility_return_nil_impl(std::false_type) { return v8::Handle(); } inline compatibility_return_type compatibility_return() { return compatibility_return_nil_impl(std::integral_constant()); } struct _v8_isolate_throw_exception : v8::Isolate { v8::Handle ThrowException_impl(v8::Handle v) { using namespace v8; return ThrowException(v); } }; inline void compatibility_throw_impl(v8::Isolate* isolate, v8::Local exception, std::true_type) { static_cast<_v8_isolate_throw_exception*>(isolate)->ThrowException_impl(exception); } inline v8::Handle compatibility_throw_impl(v8::Isolate* isolate, v8::Local exception, std::false_type) { return static_cast<_v8_isolate_throw_exception*>(isolate)->ThrowException_impl(exception); } inline std::conditional >::type compatibility_throw(v8::Isolate* isolate, v8::Local exception) { return compatibility_throw_impl(isolate, exception, std::integral_constant()); } inline void compatibility_throw_impl(v8::Local exception, std::true_type) { static_cast<_v8_isolate_throw_exception*>(v8::Isolate::GetCurrent())->ThrowException_impl(exception); } inline v8::Handle compatibility_throw_impl(v8::Local exception, std::false_type) { return static_cast<_v8_isolate_throw_exception*>(v8::Isolate::GetCurrent())->ThrowException_impl(exception); } inline std::conditional >::type compatibility_throw(v8::Local exception) { return compatibility_throw_impl(exception, std::integral_constant()); } template v8::Local compatibility_cast(v8::Local v); template v8::Local compatibility_cast(U* v); template struct hack_private_member { /* export it ... */ typedef typename Tag::type type; static type ptr; }; template typename hack_private_member::type hack_private_member::ptr; template struct rob_private_member : hack_private_member { /* fill it ... */ struct filler { filler() { hack_private_member::ptr = p; } }; static filler filler_obj; }; template typename rob_private_member::filler rob_private_member::filler_obj; template struct persistent_base_new { typedef T*(*type)(v8::Isolate*, T*); }; template class rob_private_member, &v8::PersistentBase::New>; template v8::Local make_persistent(v8::Isolate* isolate, v8::Handle v) { v8::Value* p = hack_private_member>::ptr (isolate, *compatibility_cast(v)); return compatibility_cast(compatibility_cast(p)); } template v8::Local make_weak(v8::Isolate* isolate, v8::Handle v, F&& f) { v8::Value* p = hack_private_member>::ptr (isolate, *compatibility_cast(v)); v8::PersistentBase* persistent = static_cast*> (static_cast(&p)); auto callback = [](const v8::WeakCallbackInfo::type>& data) -> void { typename std::remove_reference::type* f = data.GetParameter(); (*f)(); delete f; }; persistent->SetWeak(new typename std::remove_reference::type(std::forward(f)), callback, v8::WeakCallbackType::kParameter); return compatibility_cast(compatibility_cast(p)); } template struct global_ref { global_ref() {} global_ref(v8::Local v) : _value(make_persistent(nullptr, v)) { } global_ref(v8::Isolate* isolate, v8::Local v) : _value(make_persistent(isolate, v)) { } void dispose() const { v8::PersistentBase* p = static_cast*>(static_cast(&_value)); p->Reset(); } v8::Handle handle() const { return _value; } private: mutable v8::Local _value; }; template > struct _v8_object_internal_field; template <> struct _v8_object_internal_field : v8::Object { }; inline void* GetPointerFromInternalField(int) { return nullptr; } inline void SetPointerInInternalField(int, void*) {} template <> struct _v8_object_internal_field : v8::Object { void* GetAlignedPointerFromInternalField(int index) { return GetPointerFromInternalField(index); } void SetAlignedPointerInInternalField(int index, void* p) { SetPointerInInternalField(index, p); } }; template inline T compatibility_get_pointer_internal_field(v8::Handle object, std::size_t index) { return reinterpret_cast (static_cast<_v8_object_internal_field<>*>(*object)->GetAlignedPointerFromInternalField(index)); } template inline void compatibility_set_pointer_internal_field(v8::Handle object, std::size_t index , T* pointer) { static_cast<_v8_object_internal_field<>*>(*object)->SetAlignedPointerInInternalField(index, pointer); } template struct compatibility_handle_scope_impl; template struct compatibility_handle_scope_impl : v8::HandleScope { compatibility_handle_scope_impl() : HandleScope(v8::Isolate::GetCurrent()) {} compatibility_handle_scope_impl(v8::Isolate* isolate) : HandleScope((assert(isolate != nullptr), isolate)) {} }; template struct compatibility_handle_scope_impl : v8::HandleScope { compatibility_handle_scope_impl() {} compatibility_handle_scope_impl(v8::Isolate*) {} }; using compatibility_handle_scope = compatibility_handle_scope_impl<>; template struct _v8_initialize_icu; template <> struct _v8_initialize_icu : v8::V8 { }; template <> struct _v8_initialize_icu : v8::V8 { static bool InitializeICU(const char* = NULL) { return true; } }; inline void compatibility_initialize() { #ifdef HAVE_V8_CREATE_PARAMS constexpr const char* argv[] = {""}; static_cast<_v8_initialize_icu<>*>(nullptr)->InitializeICU(); v8::V8::InitializeExternalStartupData(argv[0]); v8::V8::Initialize(); #else v8::V8::Initialize(); static_cast<_v8_initialize_icu<>*>(nullptr)->InitializeICU(); #endif } template v8::Local compatibility_cast(v8::Local v) { static_assert(sizeof(v8::Local) == sizeof(v8::Local), ""); v8::Local l; std::memcpy(&l, &v, sizeof(v8::Local)); return l; } template v8::Local compatibility_cast(U* v) { static_assert(sizeof(v8::Local) == sizeof(U*), ""); v8::Local l; std::memcpy(&l, &v, sizeof(v8::Local)); return l; } template struct _v8_get_current_context; template struct _v8_get_current_context : v8::Context { }; template struct _v8_get_current_context : T { static v8::Local GetCurrent() { return T::GetCurrent()->GetCurrentContext(); } }; inline v8::Local new_v8_external_instance(v8::Handle& ctor, void const* v, v8::Isolate* isolate) { // TODO: ensure v8::External ownership ??? (memory leak in case NewInstance throws) v8::Handle a[] = {efl::eina::js::compatibility_new(isolate, const_cast(v))}; return ctor->NewInstance(1, a); } inline compatibility_return_type cast_function(compatibility_callback_info_type args); inline v8::Local new_v8_external_instance(v8::Handle& ctor, Eo const* v, v8::Isolate* isolate) { // TODO: ensure v8::External ownership ??? (memory leak in case NewInstance throws) v8::Handle a[] = {efl::eina::js::compatibility_new(isolate, const_cast(v))}; auto obj = ctor->NewInstance(1, a); obj->Set(compatibility_new(isolate, "cast"), compatibility_new(isolate, &cast_function)->GetFunction()); return obj; } inline v8::Local compatibility_global() { return _v8_get_current_context<>::GetCurrent()->Global(); } EAPI extern std::map> constructors_map_; inline v8::Handle get_class_constructor(std::string const& class_name) { auto it = constructors_map_.find(class_name); if (it == constructors_map_.end()) throw std::runtime_error("Class not found"); return it->second; } inline void register_class_constructor(std::string const& class_name, v8::Handle constructor_ptr) { // TODO: check if already exist? constructors_map_[class_name] = constructor_ptr; } template typename std::enable_if>::type compatibility_current_stack_trace(v8::Isolate*, int frame_limit, v8::StackTrace::StackTraceOptions options) { return T::CurrentStackTrace(frame_limit, options); } template typename std::enable_if>::type compatibility_current_stack_trace(v8::Isolate *isolate, int frame_limit, v8::StackTrace::StackTraceOptions options) { return T::CurrentStackTrace(isolate, frame_limit, options); } inline compatibility_return_type cast_function(compatibility_callback_info_type args) { auto isolate = args.GetIsolate(); compatibility_handle_scope scope(isolate); v8::Local type; if(args.Length() == 1 && (type = args[0])->IsString()) { v8::Local self = args.This(); v8::Local external = self->GetInternalField(0); Eo* eo = static_cast(v8::External::Cast(*external)->Value()); v8::String::Utf8Value str(type->ToString()); char* class_name = *str; auto ctor = ::efl::eina::js::get_class_constructor(class_name); return compatibility_return (new_v8_external_instance(ctor, ::eo_ref(eo), isolate), args); } else { eina::js::compatibility_throw (isolate, v8::Exception::TypeError (eina::js::compatibility_new(isolate, "Type expected is different. Expected String type"))); return compatibility_return(); } } } } } #endif