384 lines
15 KiB
C++
384 lines
15 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_PROPERTY_DEFINITION_HH
|
||
|
#define EOLIAN_MONO_PROPERTY_DEFINITION_HH
|
||
|
|
||
|
#include "grammar/generator.hpp"
|
||
|
#include "grammar/klass_def.hpp"
|
||
|
|
||
|
#include "grammar/indentation.hpp"
|
||
|
#include "grammar/list.hpp"
|
||
|
#include "grammar/alternative.hpp"
|
||
|
#include "type.hh"
|
||
|
#include "parameter.hh"
|
||
|
#include "name_helpers.hh"
|
||
|
#include "using_decl.hh"
|
||
|
#include "blacklist.hh"
|
||
|
|
||
|
#include <eina_variant.hh>
|
||
|
|
||
|
namespace eolian_mono {
|
||
|
|
||
|
struct compare_get_and_set_value_type
|
||
|
{
|
||
|
inline bool operator () (attributes::parameter_def const& get, attributes::parameter_def const& set) const;
|
||
|
inline bool operator () (attributes::type_def const& get, attributes::type_def const& set) const;
|
||
|
};
|
||
|
|
||
|
struct compare_get_and_set_value_type_overload
|
||
|
{
|
||
|
template <typename T, typename U>
|
||
|
bool operator()(T const& /*left*/, U const& /*right*/) const
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
bool operator()(attributes::regular_type_def const& left, attributes::regular_type_def const& right) const
|
||
|
{
|
||
|
return left.base_type == right.base_type
|
||
|
&& left.namespaces == right.namespaces;
|
||
|
}
|
||
|
bool operator()(attributes::complex_type_def const& left, attributes::complex_type_def const& right) const
|
||
|
{
|
||
|
return (*this)(left.outer, right.outer)
|
||
|
&& std::equal (left.subtypes.begin(), left.subtypes.end(), right.subtypes.begin()
|
||
|
, compare_get_and_set_value_type{});
|
||
|
}
|
||
|
bool operator()(attributes::klass_name const& left, attributes::klass_name const& right) const
|
||
|
{
|
||
|
return left.namespaces == right.namespaces
|
||
|
&& left.eolian_name == right.eolian_name;
|
||
|
}
|
||
|
|
||
|
typedef bool result_type;
|
||
|
};
|
||
|
|
||
|
inline bool compare_get_and_set_value_type::operator () (attributes::parameter_def const& get, attributes::parameter_def const& set) const
|
||
|
{
|
||
|
return efl::eina::visit(compare_get_and_set_value_type_overload{}, get.type.original_type, set.type.original_type);
|
||
|
}
|
||
|
inline bool compare_get_and_set_value_type::operator () (attributes::type_def const& get, attributes::type_def const& set) const
|
||
|
{
|
||
|
return efl::eina::visit(compare_get_and_set_value_type_overload{}, get.original_type, set.original_type);
|
||
|
}
|
||
|
|
||
|
template <typename Context>
|
||
|
bool property_generate_wrapper_both_check(attributes::property_def const& property, Context const& context)
|
||
|
{
|
||
|
if (blacklist::is_property_blacklisted(property, context))
|
||
|
return false;
|
||
|
|
||
|
bool is_interface = context_find_tag<class_context>(context).current_wrapper_kind == class_context::interface;
|
||
|
bool is_static = (property.getter.is_engaged() && property.getter->is_static)
|
||
|
|| (property.setter.is_engaged() && property.setter->is_static);
|
||
|
bool is_concrete = context_find_tag<class_context>(context).current_wrapper_kind == class_context::concrete;
|
||
|
|
||
|
if ((is_concrete || is_interface) && is_static)
|
||
|
return false;
|
||
|
|
||
|
if (!property.getter)
|
||
|
return false;
|
||
|
|
||
|
if (property.setter)
|
||
|
{
|
||
|
if (property.getter->values.size() == property.setter->values.size())
|
||
|
{
|
||
|
if (!std::equal (property.getter->values.begin(), property.getter->values.end(), property.setter->values.begin()
|
||
|
, compare_get_and_set_value_type{}))
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
template <typename Context>
|
||
|
bool property_generate_wrapper_getter(attributes::property_def const& property, Context const& context)
|
||
|
{
|
||
|
if (!property_generate_wrapper_both_check (property, context))
|
||
|
return false;
|
||
|
|
||
|
if (!property.getter->keys.empty())
|
||
|
return false;
|
||
|
|
||
|
if (property.getter->explicit_return_type != attributes::void_)
|
||
|
return false;
|
||
|
|
||
|
assert (!!property.getter.is_engaged());
|
||
|
|
||
|
bool is_interface = context_find_tag<class_context>(context).current_wrapper_kind == class_context::interface;
|
||
|
if (is_interface)
|
||
|
{
|
||
|
std::string get_scope = property.getter.is_engaged() ? eolian_mono::function_scope_get(*property.getter) : "";
|
||
|
bool is_get_public = get_scope == "public ";
|
||
|
if (!is_get_public)
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
template <typename Context>
|
||
|
bool property_generate_wrapper_setter (attributes::property_def const& property, Context const& context)
|
||
|
{
|
||
|
if (!property_generate_wrapper_both_check (property, context))
|
||
|
return false;
|
||
|
if (!property.setter)
|
||
|
return false;
|
||
|
|
||
|
if (property.setter->explicit_return_type != attributes::void_)
|
||
|
return false;
|
||
|
|
||
|
if (!property.setter->keys.empty())
|
||
|
return false;
|
||
|
|
||
|
bool is_interface = context_find_tag<class_context>(context).current_wrapper_kind == class_context::interface;
|
||
|
if (property.setter.is_engaged() && is_interface)
|
||
|
{
|
||
|
std::string set_scope = property.setter.is_engaged() ? eolian_mono::function_scope_get(*property.setter) : "";
|
||
|
bool is_set_public = set_scope == "public ";
|
||
|
if (!is_set_public)
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
struct native_property_function_definition_generator
|
||
|
{
|
||
|
template <typename OutputIterator, typename Context>
|
||
|
bool generate(OutputIterator sink, attributes::property_def const& property, Context const& context) const
|
||
|
{
|
||
|
EINA_CXX_DOM_LOG_DBG(eolian_mono::domain) << "native_property_function_definition_generator: " << property.name << std::endl;
|
||
|
|
||
|
if(blacklist::is_property_blacklisted(property, context))
|
||
|
return true;
|
||
|
|
||
|
auto const& indent = current_indentation(context);
|
||
|
|
||
|
bool has_wrapper_getter = property_generate_wrapper_getter (property, context);
|
||
|
bool has_wrapper_setter = property_generate_wrapper_setter (property, context);
|
||
|
|
||
|
auto gen = [&] (attributes::function_def const& f, bool is_set)
|
||
|
{
|
||
|
// Delegate for the C# method we will export to EO as a method implementation.
|
||
|
if(!as_generator
|
||
|
(
|
||
|
indent << eolian_mono::marshall_annotation(true) << "\n"
|
||
|
<< indent << "private delegate "
|
||
|
<< eolian_mono::marshall_type(true)
|
||
|
<< " "
|
||
|
<< string
|
||
|
<< "_delegate(" << (f.is_static ? "" : "System.IntPtr obj, System.IntPtr pd")
|
||
|
<< ((!f.is_static && f.parameters.size() > 0) ? ", " : "")
|
||
|
<< (grammar::attribute_reorder<-1, -1>
|
||
|
(
|
||
|
(marshall_annotation << " " << marshall_parameter)
|
||
|
) % ", ")
|
||
|
<< ");\n\n")
|
||
|
.generate(sink, std::make_tuple(f.return_type, f.return_type, f.c_name, f.parameters), context))
|
||
|
return false;
|
||
|
|
||
|
// API delegate is the wrapper for the Eo methods exported from C that we will use from C#.
|
||
|
if(!as_generator
|
||
|
(
|
||
|
indent << eolian_mono::marshall_annotation(true) << "\n"
|
||
|
<< indent << "internal delegate "
|
||
|
<< eolian_mono::marshall_type(true)
|
||
|
<< " "
|
||
|
<< string << "_api_delegate(" << (f.is_static ? "" : "System.IntPtr obj")
|
||
|
<< ((!f.is_static && f.parameters.size() > 0) ? ", " : "")
|
||
|
<< (grammar::attribute_reorder<-1, -1>
|
||
|
(
|
||
|
(marshall_annotation << " " << marshall_parameter)
|
||
|
) % ", ")
|
||
|
<< ");\n\n")
|
||
|
.generate(sink, std::make_tuple(f.return_type, f.return_type, f.c_name, f.parameters), context))
|
||
|
return false;
|
||
|
|
||
|
// Delegate holder (so it can't be collected).
|
||
|
if(!as_generator
|
||
|
(indent << "internal static readonly Efl.Eo.FunctionWrapper<" << string << "_api_delegate> " << string << "_ptr = new Efl.Eo.FunctionWrapper<"
|
||
|
<< string << "_api_delegate>(Module, \"" << string << "\");\n\n")
|
||
|
.generate(sink, std::make_tuple(f.c_name, f.c_name, f.c_name, f.c_name), context))
|
||
|
return false;
|
||
|
|
||
|
// We do not generate the wrapper to be called from C for non public interface member directly.
|
||
|
if (blacklist::is_non_public_interface_member(f, *implementing_klass))
|
||
|
return true;
|
||
|
|
||
|
// Do not generate static method in interface
|
||
|
if (((implementing_klass->type == attributes::class_type::interface_) ||
|
||
|
(implementing_klass->type == attributes::class_type::mixin)) && f.is_static)
|
||
|
return true;
|
||
|
|
||
|
// Actual method implementation to be called from C.
|
||
|
std::string return_type;
|
||
|
if(!as_generator(eolian_mono::type(true)).generate(std::back_inserter(return_type), f.return_type, context))
|
||
|
return false;
|
||
|
|
||
|
std::string klass_cast_name;
|
||
|
if ((implementing_klass->type == attributes::class_type::interface_) ||
|
||
|
((implementing_klass->type == attributes::class_type::mixin) && !f.is_static))
|
||
|
klass_cast_name = name_helpers::klass_interface_name(*implementing_klass);
|
||
|
else
|
||
|
klass_cast_name = name_helpers::klass_inherit_name(*implementing_klass);
|
||
|
|
||
|
std::string self = "Efl.Eo.Globals.Super(obj, Efl.Eo.Globals.GetClass(obj))";
|
||
|
|
||
|
if (f.is_static)
|
||
|
self = "";
|
||
|
|
||
|
if(!as_generator
|
||
|
(indent << "[SuppressMessage(\"Microsoft.Reliability\", \"CA2000:DisposeObjectsBeforeLosingScope\", Justification = \"The instantiated objects can be stored in the called Managed API method.\")]\n"
|
||
|
<< indent << "private static "
|
||
|
<< eolian_mono::marshall_type(true) << " "
|
||
|
<< string
|
||
|
<< "(System.IntPtr obj, System.IntPtr pd"
|
||
|
<< *(", " << marshall_parameter)
|
||
|
<< ")\n"
|
||
|
<< indent << "{\n"
|
||
|
<< indent << scope_tab << "Eina.Log.Debug(\"function " << string << " was called\");\n"
|
||
|
<< indent << scope_tab << "var ws = Efl.Eo.Globals.GetWrapperSupervisor(obj);\n"
|
||
|
<< indent << scope_tab << "if (ws != null)\n"
|
||
|
<< indent << scope_tab << "{\n"
|
||
|
<< indent << scope_tab << scope_tab << eolian_mono::native_function_definition_preamble() << "\n"
|
||
|
<< indent << scope_tab << scope_tab << "try\n"
|
||
|
<< indent << scope_tab << scope_tab << "{\n"
|
||
|
)
|
||
|
.generate(sink, std::make_tuple(f.return_type, escape_keyword(f.name), f.parameters
|
||
|
, /***/f.c_name/***/
|
||
|
, f
|
||
|
), context))
|
||
|
return false;
|
||
|
if (is_set/* && has_wrapper_setter*/)
|
||
|
{
|
||
|
if(!as_generator
|
||
|
(
|
||
|
indent << scope_tab << scope_tab << scope_tab << (return_type != "void" ? "_ret_var = " : "")
|
||
|
<< (f.is_static ? "" : "((") << klass_cast_name << (f.is_static ? "." : ")ws.Target).") << string
|
||
|
)
|
||
|
.generate(sink, std::make_tuple(name_helpers::property_managed_name(property), f.parameters), context))
|
||
|
return false;
|
||
|
|
||
|
if(!f.keys.empty() && !as_generator(lit("[(") << (native_argument_invocation % ", ") << ")]").generate (sink, f.keys, context))
|
||
|
return false;
|
||
|
|
||
|
if(!as_generator
|
||
|
(" = ("
|
||
|
<< (native_tuple_argument_invocation % ", ") << ");\n"
|
||
|
)
|
||
|
.generate(sink, f.values, context))
|
||
|
return false;
|
||
|
}
|
||
|
else if (!is_set/* && has_wrapper_getter*/)
|
||
|
{
|
||
|
if(!as_generator
|
||
|
(
|
||
|
indent << scope_tab << scope_tab << scope_tab << "var ret = "
|
||
|
<< (f.is_static ? "" : "((") << klass_cast_name << (f.is_static ? "." : ")ws.Target).")
|
||
|
<< string
|
||
|
|
||
|
)
|
||
|
.generate(sink, std::make_tuple(name_helpers::property_managed_name(property)), context))
|
||
|
return false;
|
||
|
|
||
|
if(!f.keys.empty() && !as_generator(lit("[(") << (native_argument_invocation % ", ") << ")]").generate (sink, f.keys, context))
|
||
|
return false;
|
||
|
|
||
|
if (!as_generator(";\n").generate (sink, attributes::unused, context))
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
// else if (!as_generator
|
||
|
// (indent << scope_tab << scope_tab << scope_tab << (return_type != "void" ? "_ret_var = " : "")
|
||
|
// << (f.is_static ? "" : "((") << klass_cast_name << (f.is_static ? "." : ")ws.Target).") << string
|
||
|
// << "(" << (native_argument_invocation % ", ") << ");\n"
|
||
|
// ).generate(sink, std::make_tuple(name_helpers::managed_method_name(f), f.parameters), context))
|
||
|
// return false;
|
||
|
|
||
|
if(!as_generator
|
||
|
(
|
||
|
indent << scope_tab << scope_tab << "}\n"
|
||
|
<< indent << scope_tab << scope_tab << "catch (Exception e)\n"
|
||
|
<< indent << scope_tab << scope_tab << "{\n"
|
||
|
<< indent << scope_tab << scope_tab << scope_tab << "Eina.Log.Warning($\"Callback error: {e.ToString()}\");\n"
|
||
|
<< indent << scope_tab << scope_tab << scope_tab << "Eina.Error.Set(Eina.Error.UNHANDLED_EXCEPTION);\n"
|
||
|
<< indent << scope_tab << scope_tab << "}\n\n"
|
||
|
<< indent << eolian_mono::native_function_definition_epilogue(*implementing_klass) << "\n"
|
||
|
<< indent << scope_tab << "}\n"
|
||
|
<< indent << scope_tab << "else\n"
|
||
|
<< indent << scope_tab << "{\n"
|
||
|
<< indent << scope_tab << scope_tab << (return_type != "void" ? "return " : "") << string
|
||
|
<< "_ptr.Value.Delegate(" << self << ((!f.is_static && f.parameters.size() > 0) ? ", " : "") << (argument % ", ") << ");\n"
|
||
|
<< indent << scope_tab << "}\n"
|
||
|
<< indent << "}\n\n"
|
||
|
)
|
||
|
.generate(sink, std::make_tuple(f, f.c_name, f.parameters), context))
|
||
|
return false;
|
||
|
|
||
|
// Static functions do not need to be called from C
|
||
|
if (f.is_static)
|
||
|
return true;
|
||
|
|
||
|
// This is the delegate that will be passed to Eo to be called from C.
|
||
|
if(!as_generator(
|
||
|
indent << "private static " << f.c_name << "_delegate " << f.c_name << "_static_delegate;\n\n"
|
||
|
).generate(sink, attributes::unused, context))
|
||
|
return false;
|
||
|
return true;
|
||
|
};
|
||
|
|
||
|
bool r = true;
|
||
|
if(r && property.getter && has_wrapper_getter
|
||
|
&& helpers::is_function_registerable (*property.getter, *implementing_klass))
|
||
|
r &= gen (*property.getter, false);
|
||
|
if(r && property.setter && has_wrapper_setter
|
||
|
&& helpers::is_function_registerable (*property.setter, *implementing_klass))
|
||
|
r &= gen (*property.setter, true);
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
attributes::klass_def const* implementing_klass, *klass_from_property;
|
||
|
};
|
||
|
|
||
|
struct native_property_function_definition_parameterized
|
||
|
{
|
||
|
native_property_function_definition_generator operator()(attributes::klass_def const& klass
|
||
|
, attributes::klass_def const& prop_from_klass) const
|
||
|
{
|
||
|
return {&klass, &prop_from_klass};
|
||
|
}
|
||
|
} const native_property_function_definition;
|
||
|
|
||
|
}
|
||
|
|
||
|
namespace efl { namespace eolian { namespace grammar {
|
||
|
|
||
|
template <>
|
||
|
struct is_eager_generator< ::eolian_mono::native_property_function_definition_generator> : std::true_type {};
|
||
|
template <>
|
||
|
struct is_generator< ::eolian_mono::native_property_function_definition_generator> : std::true_type {};
|
||
|
|
||
|
namespace type_traits {
|
||
|
|
||
|
template <>
|
||
|
struct attributes_needed< ::eolian_mono::native_property_function_definition_generator> : std::integral_constant<int, 1> {};
|
||
|
|
||
|
} } } }
|
||
|
|
||
|
#endif
|