/* * 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_FUNCTION_DEFINITION_HH #define EOLIAN_MONO_FUNCTION_DEFINITION_HH #include #include "grammar/generator.hpp" #include "grammar/klass_def.hpp" #include "grammar/kleene.hpp" #include "grammar/indentation.hpp" #include "grammar/list.hpp" #include "grammar/alternative.hpp" #include "grammar/attribute_reorder.hpp" #include "grammar/counter.hpp" #include "logging.hh" #include "type.hh" #include "name_helpers.hh" #include "helpers.hh" #include "function_helpers.hh" #include "marshall_type.hh" #include "parameter.hh" #include "documentation.hh" #include "using_decl.hh" #include "generation_contexts.hh" #include "blacklist.hh" namespace eolian_mono { struct native_function_definition_generator { attributes::klass_def const* klass; template bool generate(OutputIterator sink, attributes::function_def const& f, Context const& context) const { EINA_CXX_DOM_LOG_DBG(eolian_mono::domain) << "native_function_definition_generator: " << f.c_name << std::endl; if(blacklist::is_function_blacklisted(f, context)) return true; auto const& indent = current_indentation(context); // 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 << "public 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 << "public 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, *klass)) return true; // Do not generate static method in interface if (((klass->type == attributes::class_type::interface_) || (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 ((klass->type == attributes::class_type::interface_) || ((klass->type == attributes::class_type::mixin) && !f.is_static)) klass_cast_name = name_helpers::klass_interface_name(*klass); else klass_cast_name = name_helpers::klass_inherit_name(*klass); std::string self = "Efl.Eo.Globals.Super(obj, Efl.Eo.Globals.GetClass(obj))"; if (f.is_static) self = ""; if(!as_generator (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" << 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" << 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(*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.return_type, escape_keyword(f.name), f.parameters , /***/f.c_name/***/ , f , name_helpers::managed_method_name(f) , f.parameters , 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; } }; struct function_definition_generator { function_definition_generator(bool do_super = false) : do_super(do_super) {} template bool generate(OutputIterator sink, attributes::function_def const& f, Context const& context) const { EINA_CXX_DOM_LOG_DBG(eolian_mono::domain) << "function_definition_generator: " << f.c_name << std::endl; bool is_concrete = context_find_tag(context).current_wrapper_kind == class_context::concrete; if(blacklist::is_function_blacklisted(f, context)) return true; // Do not generate static function for concrete class if (is_concrete && f.is_static) return true; std::string return_type; if(!as_generator(eolian_mono::type(true)).generate(std::back_inserter(return_type), f.return_type, context)) return false; if(!as_generator (documentation(1)).generate(sink, f, context)) return false; std::string self = "this.NativeHandle"; // IsGeneratedBindingClass is set in the constructor, true if this // instance is from a pure C# class (not generated). if (do_super && !f.is_static) self = "(IsGeneratedBindingClass ? " + self + " : Efl.Eo.Globals.Super(" + self + ", this.NativeClass))"; else if (f.is_static) self = ""; if(!as_generator (scope_tab << eolian_mono::function_scope_get(f) << ((do_super && !f.is_static) ? "virtual " : "") << (f.is_static ? "static " : "") << return_type << " " << string << "(" << (parameter % ", ") << ") {\n" << scope_tab(2) << eolian_mono::function_definition_preamble() << klass_full_native_inherit_name(f.klass) << "." << string << "_ptr.Value.Delegate(" << self << ((!f.is_static && (f.parameters.size() > 0)) ? "," : "") << (argument_invocation % ", ") << ");\n" << scope_tab(2) << eolian_mono::function_definition_epilogue() << scope_tab(1) << "}\n\n") .generate(sink, std::make_tuple(name_helpers::managed_method_name(f), f.parameters, f, f.c_name, f.parameters, f), context)) return false; return true; } bool do_super; }; struct function_definition_parameterized { function_definition_generator operator()(bool do_super) const { return {do_super}; } } const function_definition; function_definition_generator as_generator(function_definition_parameterized) { return {}; } struct native_function_definition_parameterized { native_function_definition_generator operator()(attributes::klass_def const& klass) const { return {&klass}; } } const native_function_definition; struct property_extension_method_definition_generator { template bool generate(OutputIterator sink, attributes::property_def const& property, Context context) const { if (blacklist::is_property_blacklisted(property, context)) return true; auto options = efl::eolian::grammar::context_find_tag(context); if (!options.want_beta) return true; // Bindable is a beta feature for now. auto get_params = property.getter.is_engaged() ? property.getter->parameters.size() : 0; auto set_params = property.setter.is_engaged() ? property.setter->parameters.size() : 0; std::string managed_name = name_helpers::property_managed_name(property); if (get_params > 0 || set_params > 1) return true; std::string dir_mod; if (property.setter.is_engaged()) dir_mod = direction_modifier(property.setter->parameters[0]); if (property.setter.is_engaged()) { attributes::type_def prop_type = property.setter->parameters[0].type; if (!as_generator(scope_tab << "public static Efl.BindableProperty<" << type(true) << "> " << managed_name << "(this Efl.Ui.ItemFactory fac, Efl.Csharp.ExtensionTag<" << name_helpers::klass_full_concrete_or_interface_name(cls) << ", T>magic = null) where T : " << name_helpers::klass_full_concrete_or_interface_name(cls) << " {\n" << scope_tab << scope_tab << "return new Efl.BindableProperty<" << type(true) << ">(\"" << property.name << "\", fac);\n" << scope_tab << "}\n\n" ).generate(sink, std::make_tuple(prop_type, prop_type), context)) return false; } // Do we need BindablePart extensions for this class? // IContent parts are handled directly through BindableFactoryParts if (!helpers::inherits_from(cls, "Efl.Ui.LayoutPart") || helpers::inherits_from(cls, "Efl.IContent")) return true; if (property.setter.is_engaged()) { attributes::type_def prop_type = property.setter->parameters[0].type; if (!as_generator(scope_tab << "public static Efl.BindableProperty<" << type(true) << "> " << managed_name << "(this Efl.BindablePart part, Efl.Csharp.ExtensionTag<" << name_helpers::klass_full_concrete_or_interface_name(cls) << ", T>magic = null) where T : " << name_helpers::klass_full_concrete_or_interface_name(cls) << " {\n" << scope_tab << scope_tab << "return new Efl.BindableProperty<" << type(true) << ">(part.PartName, \"" << property.name << "\", part.Binder);\n" << scope_tab << "}\n\n" ).generate(sink, std::make_tuple(prop_type, prop_type), context)) return false; } return true; } grammar::attributes::klass_def const& cls; }; property_extension_method_definition_generator property_extension_method_definition (grammar::attributes::klass_def const& cls) { return {cls}; } struct property_wrapper_definition_generator { template bool generate(OutputIterator sink, attributes::property_def const& property, Context const& context) const { using efl::eolian::grammar::attribute_reorder; using efl::eolian::grammar::counter; using efl::eolian::grammar::attributes::parameter_direction; using efl::eolian::grammar::attributes::parameter_def; if (blacklist::is_property_blacklisted(property, *implementing_klass, context)) return true; bool is_interface = context_find_tag(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(context).current_wrapper_kind == class_context::concrete; if ((is_concrete || is_interface) && is_static) return true; auto get_params = property.getter.is_engaged() ? property.getter->parameters.size() : 0; //auto set_params = property.setter.is_engaged() ? property.setter->parameters.size() : 0; // C# properties must have a single value. // // Single values in getters are automatically converted to return_type, // meaning they should have 0 parameters. // // For setters, we ignore the return type - usually boolean. // if (get_params > 0 || set_params > 1) // return true; if (property.getter && std::find_if (property.getter->parameters.begin() , property.getter->parameters.end() , [] (parameter_def const& p) { return p.direction != parameter_direction::out; }) != property.getter->parameters.end()) return true; if (property.setter && std::find_if (property.setter->parameters.begin() , property.setter->parameters.end() , [] (parameter_def const& p) { return p.direction != parameter_direction::in; }) != property.setter->parameters.end()) return true; if (property.getter && property.setter) { if (get_params != 0 && property.setter->parameters.size() != property.getter->parameters.size()) return true; } std::vector parameters; if (property.setter.is_engaged()) { std::transform (property.setter->parameters.begin(), property.setter->parameters.end() , std::back_inserter(parameters) , [] (parameter_def p) -> parameter_def { //p.direction = efl::eolian::attributes::parameter_direction::in; return p; }); } else if (property.getter.is_engaged()) { // if getter has parameters, then we ignore return type, otherwise // we use the return type. if (get_params == 0) parameters.push_back({parameter_direction::in , property.getter->return_type, "propertyResult", {} , property.getter->unit}); else std::transform (property.getter->parameters.begin(), property.getter->parameters.end() , std::back_inserter(parameters) , [] (parameter_def p) -> parameter_def { p.direction = parameter_direction::in; return p; }); } else { EINA_CXX_DOM_LOG_ERR(eolian_mono::domain) << "Property must have either a getter or a setter." << std::endl; return false; } std::string dir_mod; if (property.setter.is_engaged()) dir_mod = direction_modifier(property.setter->parameters[0]); std::string managed_name = name_helpers::property_managed_name(property); std::string scope = "public "; std::string get_scope = property.getter.is_engaged() ? eolian_mono::function_scope_get(*property.getter) : ""; bool is_get_public = get_scope == "public "; std::string set_scope = property.setter.is_engaged() ? eolian_mono::function_scope_get(*property.setter) : ""; bool is_set_public = set_scope == "public "; // No need to generate this wrapper as no accessor is public. if (is_interface && (!is_get_public && !is_set_public)) return true; // C# interface members are declared automatically as public if (is_interface) { scope = ""; get_scope = ""; set_scope = ""; } else if ((get_scope != "") && (get_scope == set_scope)) { scope = get_scope; get_scope = ""; set_scope = ""; } else if (!property.setter.is_engaged() || (get_scope == scope)) { scope = get_scope; get_scope = ""; } else if (!property.getter.is_engaged() || (set_scope == scope)) { scope = set_scope; set_scope = ""; } if (parameters.size() == 1) { if (!as_generator( documentation(1) << scope_tab << scope << (is_static ? "static " : "") << type(true) << " " << managed_name << " {\n" ).generate(sink, std::make_tuple(property, parameters[0].type), context)) return false; } else { if (!as_generator ( documentation(1) << scope_tab << scope << (is_static ? "static (" : "(") << (attribute_reorder<1, -1>(type(true) /*<< " " << argument*/) % ", ") << ") " << managed_name << " {\n" ).generate(sink, std::make_tuple(property, parameters), context)) return false; } if (property.getter.is_engaged() && is_interface) { if (is_get_public) if (!as_generator(scope_tab << scope_tab << set_scope << "get;\n" ).generate(sink, attributes::unused, context)) return false; } else if (property.getter.is_engaged() && get_params == 0/*parameters.size() == 1 && property.getter.is_engaged()*/) { if (!as_generator (scope_tab << scope_tab << get_scope << "get " << "{ return " + name_helpers::managed_method_name(*property.getter) + "(); }\n" ).generate(sink, attributes::unused, context)) return false; } else if (parameters.size() >= 1 && property.getter) { if (!as_generator (scope_tab << scope_tab << get_scope << "get " << "{\n" << *attribute_reorder<1, -1, 1> (scope_tab(3) << type(true) << " _out_" << argument(false) << " = default(" << type(true) << ");\n" ) << scope_tab(3) << name_helpers::managed_method_name(*property.getter) << "(" << (("out _out_" << argument(false)) % ",") << ");\n" << scope_tab(3) << "return (" << (("_out_"<< argument(false)) % ",") << ");\n" << scope_tab(2) << "}" << "\n" ).generate(sink, std::make_tuple(parameters, parameters, parameters), context)) return false; } // else if (parameters.size() == 1) // { // if (!as_generator // (scope_tab << scope_tab << get_scope << "get " // << "{\n" // << *attribute_reorder<1, -1, 1>(scope_tab(3) << type(true) << " _out_" << argument(false) << " = default(" << type(true) << ");\n") // << scope_tab(3) << name_helpers::managed_method_name(*property.getter) // << "(" << (("out _out_" << argument(false)) % ",") << ");\n" // << scope_tab(3) << "return " << (("_out_"<< argument(false)) % ",") << ";\n" // << scope_tab(2) << "}" << "\n" // ).generate(sink, std::make_tuple(parameters, parameters, parameters), context)) // return false; // } if (property.setter.is_engaged() && is_interface) { if (is_set_public) if (!as_generator(scope_tab << scope_tab << set_scope << "set;\n" ).generate(sink, attributes::unused, context)) return false; } else if (parameters.size() == 1 && property.setter.is_engaged()) { if (!as_generator(scope_tab << scope_tab << set_scope << "set " << "{ " + name_helpers::managed_method_name(*property.setter) + "(" + dir_mod + "value); }\n" ).generate(sink, attributes::unused, context)) return false; } else if (parameters.size() > 1 && property.setter.is_engaged()) { if (!as_generator(scope_tab << scope_tab << set_scope << "set " << ("{ " + name_helpers::managed_method_name(*property.setter) + "(" + dir_mod) << ((" value.Item" << counter(1)) % ", ") << "); }" << "\n" ).generate(sink, parameters, context)) return false; } if (!as_generator(scope_tab << "}\n\n").generate(sink, attributes::unused, context)) return false; return true; } attributes::klass_def const* implementing_klass; }; struct property_wrapper_definition_parameterized { property_wrapper_definition_generator operator()(attributes::klass_def const& klass) const { return {&klass}; } } const property_wrapper_definition; property_wrapper_definition_generator as_generator(property_wrapper_definition_parameterized) { return {}; } } namespace efl { namespace eolian { namespace grammar { template <> struct is_eager_generator< ::eolian_mono::function_definition_generator> : std::true_type {}; template <> struct is_eager_generator< ::eolian_mono::native_function_definition_generator> : std::true_type {}; template <> struct is_eager_generator< ::eolian_mono::property_extension_method_definition_generator> : std::true_type {}; template <> struct is_eager_generator< ::eolian_mono::property_wrapper_definition_generator> : std::true_type {}; template <> struct is_eager_generator< ::eolian_mono::property_wrapper_definition_parameterized> : std::true_type {}; template <> struct is_generator< ::eolian_mono::function_definition_generator> : std::true_type {}; template <> struct is_generator< ::eolian_mono::native_function_definition_generator> : std::true_type {}; template <> struct is_generator< ::eolian_mono::function_definition_parameterized> : std::true_type {}; template <> struct is_generator< ::eolian_mono::property_extension_method_definition_generator> : std::true_type {}; template <> struct is_generator< ::eolian_mono::property_wrapper_definition_generator> : std::true_type {}; template <> struct is_generator< ::eolian_mono::property_wrapper_definition_parameterized> : std::true_type {}; namespace type_traits { template <> struct attributes_needed< ::eolian_mono::function_definition_generator> : std::integral_constant {}; template <> struct attributes_needed< ::eolian_mono::function_definition_parameterized> : std::integral_constant {}; template <> struct attributes_needed< ::eolian_mono::native_function_definition_generator> : std::integral_constant {}; template <> struct attributes_needed< ::eolian_mono::property_extension_method_definition_generator> : std::integral_constant {}; template <> struct attributes_needed< ::eolian_mono::property_wrapper_definition_generator> : std::integral_constant {}; template <> struct attributes_needed< ::eolian_mono::property_wrapper_definition_parameterized> : std::integral_constant {}; } } } } #endif