diff options
Diffstat (limited to 'src/bindings/eina_js/eina_js_container.cc')
-rw-r--r-- | src/bindings/eina_js/eina_js_container.cc | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/src/bindings/eina_js/eina_js_container.cc b/src/bindings/eina_js/eina_js_container.cc new file mode 100644 index 0000000000..60c53e0e74 --- /dev/null +++ b/src/bindings/eina_js/eina_js_container.cc @@ -0,0 +1,495 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <Eina.hh> +#include <Eina.h> +#include <cstdlib> + +#include <Eo.h> + +#include <eina_integer_sequence.hh> +#include <eina_tuple.hh> +#include <eina_ptrlist.hh> + +#include <Eina_Js.hh> + +#include <tuple> + +#include <iostream> + +namespace efl { namespace eina { namespace js { + +template <typename T> +struct tag { typedef T type; }; + +namespace { + +global_ref<v8::ObjectTemplate> instance_persistents[container_type_size]; +global_ref<v8::Function> instance_templates[container_type_size]; + +v8::Local<v8::Value> push(eina_container_base& self, v8::Isolate* isolate, v8::Local<v8::Value> value) +{ + return v8::Integer::New(isolate, self.push(isolate, value)); +} + +v8::Local<v8::Value> pop(eina_container_base& self, v8::Isolate* isolate, v8::Local<v8::Value>) +{ + return self.pop(isolate); +} + +v8::Local<v8::Value> concat(eina_container_base& lhs, v8::Isolate* isolate, v8::Local<v8::Value> other) +{ + const char* error_message = 0; + if(other->IsObject()) + { + v8::Local<v8::Object> obj = v8::Local<v8::Object>::Cast(other); + v8::String::Utf8Value constructor_name (obj->GetConstructorName()); + if(obj->GetConstructorName()->Equals(efl::eina::js::compatibility_new<v8::String>(isolate, "eina_list")) + || obj->GetConstructorName()->Equals(efl::eina::js::compatibility_new<v8::String>(isolate, "eina_array"))) + { + eina_container_base& rhs = *static_cast<eina_container_base*> + (efl::eina::js::compatibility_get_pointer_internal_field(obj, 0)); + std::type_info const& typeinfo_lhs = typeid(lhs) + , &typeinfo_rhs = typeid(rhs); + if(!typeinfo_lhs.before(typeinfo_rhs) && !typeinfo_rhs.before(typeinfo_lhs)) + { + v8::Handle<v8::Value> a[] = + {efl::eina::js::compatibility_new<v8::External>(isolate, rhs.concat(lhs))}; + assert(!!*instance_templates[lhs.get_container_type()].handle()); + v8::Local<v8::Object> result = + instance_templates[lhs.get_container_type()].handle()->NewInstance(1, a); + return result; + } + else + error_message = "Containers are not of the same type."; + } + else + error_message = "Object to be concatenated is not a container."; + } + else + error_message = "Concatenation argument is not an container"; + + eina::js::compatibility_throw + (isolate, v8::Exception::TypeError(eina::js::compatibility_new<v8::String>(isolate, error_message))); + + return v8::Undefined(isolate); +} + +v8::Local<v8::Value> slice(eina_container_base& self, v8::Isolate* isolate, v8::Local<v8::Value> begin + , v8::Local<v8::Value> end) +{ + std::size_t i, j; + + if(begin->IsUint32() || begin->IsInt32()) + i = begin->IntegerValue(); + else if (begin->IsUndefined()) + i = 0; + else + return v8::Undefined(isolate); + + if(end->IsUint32() || end->IsInt32()) + j = end->IntegerValue(); + else if (end->IsUndefined()) + j = self.size(); + else + return v8::Undefined(isolate); + + v8::Handle<v8::Value> a[] = {efl::eina::js::compatibility_new<v8::External>(isolate, self.slice(i, j))}; + v8::Local<v8::Object> result = instance_templates[self.get_container_type()].handle() + ->NewInstance(1, a); + return result; +} + +compatibility_accessor_getter_return_type length + (v8::Local<v8::String>, compatibility_accessor_getter_callback_info_type info) +{ + v8::Local<v8::Object> self_obj = compatibility_cast<v8::Object>(info.This()); + eina_container_base* self = static_cast<eina_container_base*> + (compatibility_get_pointer_internal_field(self_obj, 0)); + return compatibility_return(js::get_value_from_c(self->size(), info.GetIsolate(), ""), info); +} + +compatibility_indexed_property_getset_return_type index_get + (uint32_t index, compatibility_indexed_property_callback_info_type info) +{ + v8::Local<v8::Object> self_obj = v8::Local<v8::Object>::Cast(info.This()); + eina_container_base* self = static_cast<eina_container_base*> + (compatibility_get_pointer_internal_field(self_obj, 0)); + return compatibility_return(self->get(info.GetIsolate(), index), info); +} + +compatibility_indexed_property_getset_return_type index_set + (uint32_t index, v8::Local<v8::Value> value, compatibility_indexed_property_callback_info_type info) +{ + v8::Local<v8::Object> self_obj = v8::Local<v8::Object>::Cast(info.This()); + eina_container_base* self = static_cast<eina_container_base*> + (compatibility_get_pointer_internal_field(self_obj, 0)); + + return compatibility_return(self->set(info.GetIsolate(), index, value), info); +} + + +#define GENERATE_CONTAINER_CONSTRUCT_TYPE_IF(x) GENERATE_CONTAINER_CONSTRUCT_TYPE2_IF(x, x) +#define GENERATE_CONTAINER_CONSTRUCT_TYPE2_IF(x, y) if (!strcmp(class_name, #x)) \ + return new Container<y, nonclass_cls_name_getter, typename container_wrapper<y>::type>(); + +template< template<typename, typename, typename> class Container> +eina_container_base* construct_container(const char* class_name) +{ + GENERATE_CONTAINER_CONSTRUCT_TYPE_IF(int); + GENERATE_CONTAINER_CONSTRUCT_TYPE_IF(float); + GENERATE_CONTAINER_CONSTRUCT_TYPE2_IF(bool, Eina_Bool); + GENERATE_CONTAINER_CONSTRUCT_TYPE2_IF(string, char*); + + return 0; +} + +compatibility_return_type new_eina_list_internal(compatibility_callback_info_type args) +{ + if(args.IsConstructCall()) + { + if(args.Length() == 0) + { + eina_container_base* p = new eina_list<int>; + compatibility_set_pointer_internal_field + (args.This(), 0, dynamic_cast<void*>(p)); + } + else + { + if(args[0]->IsExternal()) + { + eina_container_base* base = reinterpret_cast<eina_container_base*> + (v8::External::Cast(*args[0])->Value()); + compatibility_set_pointer_internal_field + (args.This(), 0, dynamic_cast<void*>(base)); + } + else + std::abort(); + } + } + else + std::abort(); + return compatibility_return(); +} + +compatibility_return_type new_eina_list(compatibility_callback_info_type args) +{ + if(args.IsConstructCall()) + { + if(args.Length() == 0) // Default constructor, list of ints. Or should be list of Eo's? + { + eina_container_base* p = new eina_list<int>; + compatibility_set_pointer_internal_field + (args.This(), 0, dynamic_cast<void*>(p)); + return compatibility_return(); + } + else if (args.Length() == 1 && args[0]->IsString()) + { + v8::String::Utf8Value string(args[0]); + eina_container_base* p = construct_container<efl::eina::js::eina_list>(*string); + if (!p) { + return eina::js::compatibility_throw + (args.GetIsolate(), v8::Exception::TypeError + (eina::js::compatibility_new<v8::String>(args.GetIsolate(), "Invalid type for container."))); + } + compatibility_set_pointer_internal_field + (args.This(), 0, dynamic_cast<void*>(p)); + return compatibility_return(); + } + } + return eina::js::compatibility_throw + (args.GetIsolate(), v8::Exception::TypeError + (eina::js::compatibility_new<v8::String>(args.GetIsolate(), "Wrong number of arguments for constructor call"))); +} + +compatibility_return_type new_eina_array_internal(compatibility_callback_info_type args) +{ + if(args.IsConstructCall()) + { + if(args.Length() == 0) + { + eina_container_base* p = new eina_array<int>; + compatibility_set_pointer_internal_field + (args.This(), 0, dynamic_cast<void*>(p)); + } + else + { + if(args[0]->IsExternal()) + { + eina_container_base* base = reinterpret_cast<eina_container_base*> + (v8::External::Cast(*args[0])->Value()); + compatibility_set_pointer_internal_field + (args.This(), 0, dynamic_cast<void*>(base)); + } + else + std::abort(); + } + } + else + std::abort(); + return compatibility_return(); +} + +compatibility_return_type new_eina_array(compatibility_callback_info_type args) +{ + if(args.IsConstructCall()) + { + if(args.Length() == 0) + { + eina_container_base* p = new eina_array<int>; + compatibility_set_pointer_internal_field + (args.This(), 0, dynamic_cast<void*>(p)); + return compatibility_return(); + } + else if (args.Length() == 1 && args[0]->IsString()) + { + v8::String::Utf8Value string(args[0]); + eina_container_base* p = construct_container<efl::eina::js::eina_array>(*string); + if (!p) { + return eina::js::compatibility_throw + (args.GetIsolate(), v8::Exception::TypeError + (eina::js::compatibility_new<v8::String>(args.GetIsolate(), "Invalid type for container."))); + } + compatibility_set_pointer_internal_field + (args.This(), 0, dynamic_cast<void*>(p)); + return compatibility_return(); + } + } + return eina::js::compatibility_throw + (args.GetIsolate(), v8::Exception::TypeError + (eina::js::compatibility_new<v8::String>(args.GetIsolate(), "Wrong number of arguments for constructor call"))); +} + +template <typename F> +struct function_params; + +template <typename R, typename... Sig> +struct function_params<R(*)(Sig...)> +{ + typedef std::tuple<Sig...> type; +}; + +template <typename F> +struct function_result; + +template <typename R, typename... Sig> +struct function_result<R(*)(Sig...)> +{ + typedef R type; +}; + +template <typename T> +struct is_persistent : std::false_type {}; + +template <typename...A> +struct is_persistent<v8::Persistent<A...> > : std::true_type {}; + +template <std::size_t I, typename Sig> +typename std::tuple_element<I, Sig>::type +get_element(v8::Isolate* isolate + , compatibility_callback_info_type args + , typename std::enable_if + <is_persistent<typename std::tuple_element<I, Sig>::type>::value>::type* = 0) +{ + return typename std::tuple_element<I, Sig>::type(isolate, args[I]); +} + +template <std::size_t I, typename Sig> +typename std::tuple_element<I, Sig>::type +get_element(v8::Isolate* /*isolate*/ + , compatibility_callback_info_type args + , typename std::enable_if + <!is_persistent<typename std::tuple_element<I, Sig>::type>::value>::type* = 0) +{ + return args[I]; +} + +template <typename Sig, typename R, typename T, typename F, std::size_t... N> +R call_impl(v8::Isolate* isolate + , compatibility_callback_info_type args + , T* self, F* f + , eina::index_sequence<N...>) +{ + assert(self != 0); + return (*f)(*self, isolate, js::get_element<N, Sig>(isolate, args)...); +} + +template <typename Sig, typename T, typename F, typename R> +compatibility_return_type call_generic_impl(compatibility_callback_info_type args, tag<R>) +{ + T* self = static_cast<T*> + (compatibility_get_pointer_internal_field<>(args.This(), 0)); + F* f = reinterpret_cast<F*>(v8::External::Cast(*args.Data())->Value()); + return compatibility_return + (js::get_value_from_c + (js::call_impl<Sig, R>(args.GetIsolate(), args, self, f + , eina::make_index_sequence<std::tuple_size<Sig>::value>()) + , args.GetIsolate(), "") + , args); +} + +template <typename Sig, typename T, typename F> +compatibility_return_type call_generic_impl(compatibility_callback_info_type args, tag<void>) +{ + T* self = static_cast<T*> + (compatibility_get_pointer_internal_field(args.This(), 0)); + F* f = reinterpret_cast<F*>(v8::External::Cast(*args.Data())->Value()); + js::call_impl<Sig, void>(args.GetIsolate(), args, self, f + , eina::make_index_sequence<std::tuple_size<Sig>::value>()); + return compatibility_return(); +} + +template <typename Sig, typename R, typename T, typename F> +compatibility_return_type call_generic(compatibility_callback_info_type args) +{ + return efl::eina::js::call_generic_impl<Sig, T, F>(args, tag<R>()); +} + +template <typename Sig, typename T, typename F, typename R> +compatibility_return_type call_function_impl(compatibility_callback_info_type args, tag<R>) +{ + T* self = static_cast<T*> + (compatibility_get_pointer_internal_field(args.This(), 0)); + F f = reinterpret_cast<F>(v8::External::Cast(*args.Data())->Value()); + return compatibility_return + (/*js::get_value_from_c*/ + (js::call_impl<Sig, R>(args.GetIsolate(), args, self, f + , eina::make_index_sequence<std::tuple_size<Sig>::value>()) + /*, args.GetIsolate(), ""*/) + , args); +} + +template <typename Sig, typename T, typename F> +compatibility_return_type call_function_impl(compatibility_callback_info_type args, tag<void>) +{ + T* self = static_cast<T*> + (compatibility_get_pointer_internal_field(args.This(), 0)); + F f = reinterpret_cast<F>(v8::External::Cast(*args.Data())->Value()); + js::call_impl<Sig, void>(args.GetIsolate(), args, self, f + , eina::make_index_sequence<std::tuple_size<Sig>::value>()); + return compatibility_return(); +} + +template <typename Sig, typename R, typename T, typename F> +compatibility_return_type call_function(compatibility_callback_info_type args) +{ + return efl::eina::js::call_function_impl<Sig, T, F>(args, tag<R>()); +} + +template <typename T, typename F> +void register_(v8::Isolate* isolate, const char* name, F f, v8::Handle<v8::ObjectTemplate> template_ + , typename std::enable_if<std::is_function<typename std::remove_pointer<F>::type>::value>::type* = 0) +{ + template_->Set(compatibility_new<v8::String>(isolate, name) + , compatibility_new<v8::FunctionTemplate> + (isolate, &efl::eina::js::call_function + <typename eina::_mpl::pop_front<typename function_params<F>::type, 2u>::type + , typename function_result<F>::type, T, F> + , compatibility_new<v8::External> + (isolate, reinterpret_cast<void*>(f)))); +} + +template <typename T, typename...Sig, typename F> +void register_(v8::Isolate* isolate, const char* name, F&& f, v8::Handle<v8::ObjectTemplate> template_ + , typename std::enable_if<!std::is_function<typename std::remove_pointer<F>::type>::value>::type* = 0) +{ + using result_type = decltype + (std::declval<F>() + (std::declval<T&>(), std::declval<v8::Isolate*>() + , std::declval<Sig>()...) + ); + template_->Set(compatibility_new<v8::String>(isolate, name) + , compatibility_new<v8::FunctionTemplate> + (isolate + , &efl::eina::js::call_generic<std::tuple<Sig...>, result_type, T, F> + , compatibility_new<v8::External> + (isolate, new F(std::forward<F>(f))))); +} + +v8::Local<v8::ObjectTemplate> register_template(v8::Isolate* isolate, v8::Handle<v8::FunctionTemplate> constructor) +{ + v8::Local<v8::ObjectTemplate> instance_t = constructor->InstanceTemplate(); + instance_t->SetInternalFieldCount(1); + + instance_t->SetIndexedPropertyHandler(& efl::eina::js::index_get, & efl::eina::js::index_set); + + v8::Local<v8::ObjectTemplate> prototype = constructor->PrototypeTemplate(); + prototype->SetAccessor(compatibility_new<v8::String>(isolate, "length"), &efl::eina::js::length); + + using namespace std::placeholders; + js::register_<js::eina_container_base> + (isolate, "push", &js::push, prototype); + js::register_<js::eina_container_base> + (isolate, "pop", &js::pop, prototype); + js::register_<js::eina_container_base> + (isolate, "concat", &js::concat, prototype); + js::register_<js::eina_container_base> + (isolate, "slice", &js::slice, prototype); + js::register_<js::eina_container_base> + (isolate, "toString", std::bind(&js::eina_container_base::to_string, _1, _2), prototype); + js::register_<js::eina_container_base, v8::Local<v8::Value> > + (isolate, "join", std::bind(&js::eina_container_base::join, _1, _2, _3), prototype); + js::register_<js::eina_container_base, v8::Local<v8::Value> > + (isolate, "indexOf", std::bind(&js::eina_container_base::index_of, _1, _2, _3), prototype); + js::register_<js::eina_container_base, v8::Local<v8::Value> > + (isolate, "lastIndexOf", std::bind(&js::eina_container_base::last_index_of, _1, _2, _3), prototype); + + return instance_t; +} + +void register_class(v8::Isolate* isolate, container_type type, const char* class_name + , compatibility_function_callback callback) +{ + v8::Handle<v8::FunctionTemplate> constructor + = compatibility_new<v8::FunctionTemplate>(isolate, callback); + constructor->SetClassName(compatibility_new<v8::String>(isolate, class_name)); + + v8::Local<v8::ObjectTemplate> instance_t = efl::eina::js::register_template(isolate, constructor); + + efl::eina::js::instance_persistents[type] = global_ref<v8::ObjectTemplate>{isolate, instance_t}; + efl::eina::js::instance_templates[type] = global_ref<v8::Function>{isolate, constructor->GetFunction()}; +} +void register_class(v8::Isolate* isolate, container_type, const char* class_name_ + , const char* constructor_name_ + , compatibility_function_callback callback + , v8::Handle<v8::Object> exports) +{ + v8::Handle<v8::FunctionTemplate> constructor + = compatibility_new<v8::FunctionTemplate>(isolate, callback); + auto class_name = compatibility_new<v8::String>(isolate, class_name_); + auto constructor_name = compatibility_new<v8::String>(isolate, constructor_name_); + constructor->SetClassName(class_name); + + v8::Local<v8::ObjectTemplate> instance_t = efl::eina::js::register_template(isolate, constructor); + (void)instance_t; + + exports->Set(constructor_name, constructor->GetFunction()); +} + +} + +EAPI v8::Handle<v8::Function> get_list_instance_template() +{ + return efl::eina::js::instance_templates[efl::eina::js::list_container_type].handle(); +} + +EAPI v8::Handle<v8::Function> get_array_instance_template() +{ + return efl::eina::js::instance_templates[efl::eina::js::array_container_type].handle(); +} + +} } } + +EAPI void eina_container_register(v8::Handle<v8::Object> exports, v8::Isolate* isolate) +{ + efl::eina::js::register_class(isolate, efl::eina::js::list_container_type + , "eina_list", &efl::eina::js::new_eina_list_internal); + efl::eina::js::register_class(isolate, efl::eina::js::list_container_type + , "eina_list", "List", &efl::eina::js::new_eina_list, exports); + efl::eina::js::register_class(isolate, efl::eina::js::array_container_type + , "eina_array", &efl::eina::js::new_eina_array_internal); + efl::eina::js::register_class(isolate, efl::eina::js::array_container_type + , "eina_array", "Array", &efl::eina::js::new_eina_array, exports); +} + |