forked from enlightenment/efl
313 lines
11 KiB
C++
313 lines
11 KiB
C++
/*
|
|
* 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<attributes::regular_type_def>(&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<std::string> 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<attributes::klass_name, attributes::compare_klass_name_by_name> interface_inherits(attributes::klass_def const& cls)
|
|
{
|
|
std::set<attributes::klass_name, attributes::compare_klass_name_by_name> inherits;
|
|
|
|
std::function<void(attributes::klass_name const&)> 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<typename Context>
|
|
std::set<attributes::klass_name, attributes::compare_klass_name_by_name> non_implemented_interfaces(attributes::klass_def const& cls, Context const& context)
|
|
{
|
|
auto options = efl::eolian::grammar::context_find_tag<options_context>(context);
|
|
std::set<attributes::klass_name, attributes::compare_klass_name_by_name> implemented_interfaces;
|
|
std::set<attributes::klass_name, attributes::compare_klass_name_by_name> interfaces;
|
|
|
|
std::function<void(attributes::klass_name const&, bool)> 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<bool(attributes::klass_name const&)> 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<typename Context>
|
|
std::vector<attributes::function_def> get_all_implementable_methods(attributes::klass_def const& cls, Context const& context)
|
|
{
|
|
bool want_beta = efl::eolian::grammar::context_find_tag<options_context>(context).want_beta;
|
|
std::vector<attributes::function_def> 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<attributes::klass_name, attributes::compare_klass_name_by_name> implemented_interfaces;
|
|
std::set<attributes::klass_name, attributes::compare_klass_name_by_name> interfaces;
|
|
std::function<void(attributes::klass_name const&, bool)> 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<typename Klass>
|
|
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<typename Context>
|
|
std::vector<attributes::function_def> get_all_registerable_methods(attributes::klass_def const& cls, Context const& context)
|
|
{
|
|
std::vector<attributes::function_def> 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<attributes::constructor_def> reorder_constructors(std::vector<attributes::constructor_def> 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
|