/* * Copyright 2019 by its authors. See AUTHORS. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef EOLIAN_MONO_HELPERS_HH #define EOLIAN_MONO_HELPERS_HH #include "grammar/klass_def.hpp" #include "blacklist.hh" #include "generation_contexts.hh" #include "name_helpers.hh" namespace eolian_mono { namespace helpers { /* General helpers, not related directly with generating strings (those go in the name_helpers.hh). */ namespace attributes = efl::eolian::grammar::attributes; inline bool need_struct_conversion(attributes::regular_type_def const* regular) { return regular && regular->is_struct() && !blacklist::is_struct_blacklisted(*regular); } inline bool need_struct_conversion(attributes::parameter_def const& param, attributes::regular_type_def const* regular) { if (param.direction == attributes::parameter_direction::in && param.type.has_own) return false; return need_struct_conversion(regular); } inline bool need_struct_conversion_in_return(attributes::type_def const& ret_type, attributes::parameter_direction const& direction) { auto regular = efl::eina::get(&ret_type.original_type); if (!regular->is_struct()) return false; if (regular->is_struct() && (direction == attributes::parameter_direction::out || direction == attributes::parameter_direction::unknown)) return false; if (ret_type.has_own) return false; return true; } inline bool need_pointer_conversion(attributes::regular_type_def const* regular) { if (!regular) return false; if (regular->is_enum() || (regular->is_struct() && name_helpers::type_full_eolian_name(*regular) != "Eina.Binbuf") ) return true; std::set const types { "bool", "char" , "byte" , "short" , "int" , "long" , "llong" , "int8" , "int16" , "int32" , "int64" , "ssize" , "ubyte", "ushort", "uint", "ulong", "ullong", "uint8", "uint16", "uint32", "uint64", "size" , "ptrdiff" , "float", "double" }; if (types.find(regular->base_type) != types.end()) return true; return false; } // While klass_def has immediate_inherits, we need a way to get all interfaces inherited by an interface // either directly or through another interface. std::set interface_inherits(attributes::klass_def const& cls) { std::set inherits; std::function inherit_algo = [&] (attributes::klass_name const& klass) { // TODO we could somehow cache klass_def instantiations attributes::klass_def c(get_klass(klass, cls.unit), cls.unit); for(auto&& inherit : c.immediate_inherits) { switch(inherit.type) { case attributes::class_type::mixin: case attributes::class_type::interface_: inherits.insert(inherit); inherit_algo(inherit); break; case attributes::class_type::regular: case attributes::class_type::abstract_: inherit_algo(inherit); default: break; } } }; inherit_algo(get_klass_name(cls)); return inherits; } // Returns the set of interfaces implemented by this type that haven't been implemented // by a regular parent class. template std::set non_implemented_interfaces(attributes::klass_def const& cls, Context const& context) { auto options = efl::eolian::grammar::context_find_tag(context); std::set implemented_interfaces; std::set interfaces; std::function inherit_algo = [&] (attributes::klass_name const& klass, bool is_implemented) { // TODO we could somehow cache klass_def instantiations attributes::klass_def c(get_klass(klass, cls.unit), cls.unit); for(auto&& inherit : c.immediate_inherits) { if (inherit.is_beta && !options.want_beta) continue; switch(inherit.type) { case attributes::class_type::mixin: case attributes::class_type::interface_: interfaces.insert(inherit); if (is_implemented) implemented_interfaces.insert(inherit); inherit_algo(inherit, is_implemented); break; case attributes::class_type::abstract_: case attributes::class_type::regular: inherit_algo(inherit, true); default: break; } } }; inherit_algo(get_klass_name(cls), false); for (auto&& inherit : implemented_interfaces) interfaces.erase(inherit); return interfaces; } /* * Determines whether this class has any regular ancestor or not */ bool has_regular_ancestor(attributes::klass_def const& cls) { auto inherits = cls.inherits; std::function is_regular = [&] (attributes::klass_name const& klass) { return klass.type == attributes::class_type::regular || klass.type == attributes::class_type::abstract_; }; return std::any_of(inherits.begin(), inherits.end(), is_regular); } /* * Sugar for checking if a given class in in the inheritance tree */ bool inherits_from(attributes::klass_def const& cls, std::string const& name) { return std::any_of(cls.inherits.begin(), cls.inherits.end(), [&](attributes::klass_name const& inherit) { return name_helpers::klass_full_concrete_or_interface_name(inherit) == name; }); } /* * Gets all methods that this class should implement (i.e. that come from an unimplemented interface/mixin and the class itself) */ template std::vector get_all_implementable_methods(attributes::klass_def const& cls, Context const& context) { bool want_beta = efl::eolian::grammar::context_find_tag(context).want_beta; std::vector ret; auto filter_beta = [&want_beta](attributes::function_def const& func) { if (!want_beta) return !func.is_beta; else return true; }; std::copy_if(cls.functions.begin(), cls.functions.end(), std::back_inserter(ret), filter_beta); // Non implemented interfaces std::set implemented_interfaces; std::set interfaces; std::function inherit_algo = [&] (attributes::klass_name const &klass, bool is_implemented) { attributes::klass_def c(get_klass(klass, cls.unit), cls.unit); for (auto&& inherit: c.immediate_inherits) { switch(inherit.type) { case attributes::class_type::mixin: case attributes::class_type::interface_: interfaces.insert(inherit); if (is_implemented) implemented_interfaces.insert(inherit); inherit_algo(inherit, is_implemented); break; case attributes::class_type::abstract_: case attributes::class_type::regular: inherit_algo(inherit, true); default: break; } } }; inherit_algo(attributes::get_klass_name(cls), false); for (auto&& inherit : implemented_interfaces) interfaces.erase(inherit); for (auto&& inherit : interfaces) { attributes::klass_def klass(get_klass(inherit, cls.unit), cls.unit); std::copy_if(klass.functions.cbegin(), klass.functions.cend(), std::back_inserter(ret), filter_beta); } return ret; } template inline bool is_managed_interface(Klass const& klass) { return klass.type == attributes::class_type::interface_ || klass.type == attributes::class_type::mixin; } /* * Gets all methods that this class should register (i.e. that comes from it and non-public interface methods * that this class is the first one implementing) */ template std::vector get_all_registerable_methods(attributes::klass_def const& cls, Context const& context) { std::vector ret; auto implementable_methods = get_all_implementable_methods(cls, context); std::copy_if(implementable_methods.cbegin(), implementable_methods.cend(), std::back_inserter(ret) , [&cls](attributes::function_def const & func) { if (cls == func.klass) return true; if (is_managed_interface(func.klass) && func.is_static) return true; if (!is_managed_interface(func.klass) || func.scope != attributes::member_scope::scope_public) return true; return false; }); return ret; } /* * Checks whether the given is unique going up the inheritance tree from leaf_klass */ inline bool is_unique_event(attributes::event_def const& evt , attributes::klass_def const& leaf_klass) { auto events = leaf_klass.get_all_events(); int i = 1; return !std::any_of(events.cbegin(), events.cend(), [&evt, &i](const attributes::event_def &other) { return evt.name == other.name && i++ == 2; }); } inline std::vector reorder_constructors(std::vector constructors) { auto is_required = [](attributes::constructor_def const& ctr) { return !ctr.is_optional; }; std::stable_partition(constructors.begin(), constructors.end(), is_required); return constructors; } } // namespace helpers } // namespace eolian_mono #endif