1 /*
2  * Copyright 2019 by its authors. See AUTHORS.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #ifndef EOLIAN_MONO_FUNCTION_POINTER_HPP
17 #define EOLIAN_MONO_FUNCTION_POINTER_HPP
18 
19 #include <Eolian.h>
20 
21 #include <vector>
22 #include <string>
23 
24 #include "function_helpers.hh"
25 #include "documentation.hh"
26 #include "generation_contexts.hh"
27 #include "blacklist.hh"
28 
29 namespace eolian_mono {
30 
31 // Blacklist structs that require some kind of manual binding.
32 template <typename Context>
is_function_ptr_blacklisted(attributes::function_def const & func,Context const & context)33 static bool is_function_ptr_blacklisted(attributes::function_def const& func, Context const& context)
34 {
35   std::string name = name_helpers::function_ptr_full_eolian_name(func);
36 
37   return blacklist::is_function_blacklisted(func, context);
38 }
39 
40 struct function_pointer {
41    template <typename OutputIterator, typename Context>
generateeolian_mono::function_pointer42    bool generate(OutputIterator sink, attributes::function_def const& f, Context const& context) const
43    {
44       EINA_CXX_DOM_LOG_DBG(eolian_mono::domain) << "function_pointer_generator: " << f.name << std::endl;
45       // FIXME export Typedecl in eolian_cxx API
46       auto funcptr_ctx = context_add_tag(class_context{class_context::function_ptr}, context);
47 
48       if (is_function_ptr_blacklisted(f, context))
49         return true;
50 
51       std::string return_type;
52       if(!as_generator(eolian_mono::type(true)).generate(std::back_inserter(return_type), f.return_type, context))
53         return false;
54 
55       if (!name_helpers::open_namespaces(sink, f.namespaces, funcptr_ctx))
56         return false;
57 
58       std::string f_name = name_helpers::typedecl_managed_name(f);
59 
60       // C# visible delegate
61       if (!as_generator(documentation
62                   << "[Efl.Eo.BindingEntity]\n"
63                   << "public delegate " << type << " " << string
64                   << "(" << (parameter % ", ") << ");\n")
65               .generate(sink, std::make_tuple(f, f.return_type, f_name, f.parameters), funcptr_ctx))
66           return false;
67       // "Internal" delegate, 1-to-1 with the Unamaged function type
68       if (!as_generator(marshall_annotation(true)
69                   << "internal delegate " << marshall_type(true) << " " << string // public?
70                   << "Internal(IntPtr data" << *grammar::attribute_reorder<-1, -1>((", " << marshall_annotation << " " << marshall_parameter)) << ");\n")
71               .generate(sink, std::make_tuple(f.return_type, f.return_type, f_name, f.parameters), funcptr_ctx))
72           return false;
73 
74       // Wrapper type, with callback matching the Unamanaged one
75       if (!as_generator("internal class " << f_name << "Wrapper\n"
76                   << "{\n\n"
77                   << scope_tab << "private " << f_name  << "Internal _cb;\n"
78                   << scope_tab << "private IntPtr _cb_data;\n"
79                   << scope_tab << "private Eina.Callbacks.EinaFreeCb _cb_free_cb;\n\n"
80 
81                   << scope_tab << "internal " << f_name << "Wrapper (" << f_name << "Internal _cb, IntPtr _cb_data, Eina.Callbacks.EinaFreeCb _cb_free_cb)\n"
82                   << scope_tab << "{\n"
83                   << scope_tab << scope_tab << "this._cb = _cb;\n"
84                   << scope_tab << scope_tab << "this._cb_data = _cb_data;\n"
85                   << scope_tab << scope_tab << "this._cb_free_cb = _cb_free_cb;\n"
86                   << scope_tab << "}\n\n"
87 
88                   << scope_tab << "~" << f_name << "Wrapper()\n"
89                   << scope_tab << "{\n"
90                   << scope_tab << scope_tab << "if (this._cb_free_cb != null)\n"
91                   << scope_tab << scope_tab << "{\n"
92                   << scope_tab << scope_tab << scope_tab << "Efl.Eo.Globals.ThreadSafeFreeCbExec(this._cb_free_cb, this._cb_data);\n"
93                   << scope_tab << scope_tab << "}\n"
94                   << scope_tab << "}\n\n"
95 
96                   << scope_tab << "internal " << type << " ManagedCb(" << (parameter % ", ") << ")\n"
97                   << scope_tab << "{\n"
98                   << function_definition_preamble << "_cb(_cb_data, " << (argument_invocation % ", ") << ");\n"
99                   << function_definition_epilogue
100                   << scope_tab << "}\n\n"
101 
102 
103                   << scope_tab << marshall_annotation(true)
104                   << scope_tab << "internal static " << marshall_type(true) << " Cb(IntPtr cb_data" << *grammar::attribute_reorder<-1, -1>((", " << marshall_annotation << " " << marshall_parameter)) << ")\n"
105                   << scope_tab << "{\n"
106                   << scope_tab << scope_tab << "GCHandle handle = GCHandle.FromIntPtr(cb_data);\n"
107                   << scope_tab << scope_tab << string << " cb = (" << string << ")handle.Target;\n"
108                   << native_function_definition_preamble
109                   << scope_tab << scope_tab << "try {\n"
110                   << scope_tab << scope_tab << scope_tab <<  (return_type != "void" ? "_ret_var = " : "") << "cb(" << (native_argument_invocation % ", ") << ");\n"
111                   << scope_tab << scope_tab << "} catch (Exception e) {\n"
112                   << scope_tab << scope_tab << scope_tab << "Eina.Log.Warning($\"Callback error: {e.ToString()}\");\n"
113                   << scope_tab << scope_tab << scope_tab << "Eina.Error.Set(Eina.Error.UNHANDLED_EXCEPTION);\n"
114                   << scope_tab << scope_tab << "}\n"
115                   << native_function_definition_epilogue(nullptr)
116                   << scope_tab << "}\n"
117                   << "}\n"
118                   ).generate(sink, std::make_tuple(f.return_type, f.parameters, f, f.parameters, f, f.return_type, f.return_type, f.parameters, f_name, f_name, f, f.parameters, f), funcptr_ctx))
119           return false;
120 
121       if (!name_helpers::close_namespaces(sink, f.namespaces, funcptr_ctx))
122         return false;
123 
124       return true;
125    }
126 };
127 
128 struct function_pointer const function_pointer = {};
129 }
130 
131 #endif
132