diff --git a/src/bin/eolian_mono/eolian/mono/async_function_definition.hh b/src/bin/eolian_mono/eolian/mono/async_function_definition.hh
index b3ab6d4102..a6907a2a95 100644
--- a/src/bin/eolian_mono/eolian/mono/async_function_definition.hh
+++ b/src/bin/eolian_mono/eolian/mono/async_function_definition.hh
@@ -52,6 +52,9 @@ struct async_function_declaration_generator
return true;
if (!f.return_type.original_type.visit(is_future{}))
return true;
+ // C# interfaces can't have non-public members
+ if(f.scope != attributes::member_scope::scope_public)
+ return true;
if (!as_generator(
scope_tab << "/// Async wrapper for .\n"
diff --git a/src/bin/eolian_mono/eolian/mono/blacklist.hh b/src/bin/eolian_mono/eolian/mono/blacklist.hh
index 5099013396..c46f2f4de7 100644
--- a/src/bin/eolian_mono/eolian/mono/blacklist.hh
+++ b/src/bin/eolian_mono/eolian/mono/blacklist.hh
@@ -64,6 +64,18 @@ inline bool is_function_blacklisted(attributes::function_def const& func, Contex
return is_function_blacklisted(c_name);
}
+inline bool is_non_public_interface_member(attributes::function_def const& func, attributes::klass_def const¤t_klass)
+{
+ if (current_klass.type == attributes::class_type::interface_
+ || current_klass.type == attributes::class_type::mixin)
+ {
+ if (func.scope != attributes::member_scope::scope_public)
+ return true;
+ }
+
+ return false;
+}
+
// Blacklist structs that require some kind of manual binding.
inline bool is_struct_blacklisted(std::string const& full_name)
diff --git a/src/bin/eolian_mono/eolian/mono/documentation.hh b/src/bin/eolian_mono/eolian/mono/documentation.hh
index 588d0b68fa..6cf57fd342 100644
--- a/src/bin/eolian_mono/eolian/mono/documentation.hh
+++ b/src/bin/eolian_mono/eolian/mono/documentation.hh
@@ -73,6 +73,25 @@ struct documentation_generator
// Klass is needed to check the property naming rulles
attributes::klass_def klass_d((const ::Eolian_Class *)klass, eolian_object_unit_get(klass));
+ // Comment the block below to enable @see reference conversion for non-public interface members.
+ // As they are not generated, this causes a doc warning that fails the build, but can be useful to track
+ // public methods referencing protected stuff.
+ if (ftype != EOLIAN_PROPERTY)
+ {
+ bool is_func_public = ::eolian_function_scope_get(function, ftype) == EOLIAN_SCOPE_PUBLIC;
+
+ if (helpers::is_managed_interface(klass_d) && !is_func_public)
+ return "";
+ }
+ else
+ {
+ bool is_get_public = ::eolian_function_scope_get(function, EOLIAN_PROP_GET) == EOLIAN_SCOPE_PUBLIC;
+ bool is_set_public = ::eolian_function_scope_get(function, EOLIAN_PROP_SET) == EOLIAN_SCOPE_PUBLIC;
+
+ if (helpers::is_managed_interface(klass_d) && !(is_get_public || is_set_public))
+ return "";
+ }
+
switch(ftype)
{
case ::EOLIAN_METHOD:
@@ -112,6 +131,8 @@ struct documentation_generator
static std::string function_conversion(attributes::function_def const& func)
{
+ // This function is called only from the constructor reference conversion, so it does not
+ // need to check whether this function non-public in a interface returning an empty reference (yet).
std::string name = name_helpers::klass_full_concrete_or_interface_name(func.klass);
switch (func.type)
{
diff --git a/src/bin/eolian_mono/eolian/mono/function_declaration.hh b/src/bin/eolian_mono/eolian/mono/function_declaration.hh
index baad0bb33d..4532746be2 100644
--- a/src/bin/eolian_mono/eolian/mono/function_declaration.hh
+++ b/src/bin/eolian_mono/eolian/mono/function_declaration.hh
@@ -23,6 +23,10 @@ struct function_declaration_generator
if(blacklist::is_function_blacklisted(f, context) || f.is_static)
return true;
+ // C# interfaces can't have non-public members
+ if(f.scope != attributes::member_scope::scope_public)
+ return true;
+
if(!as_generator(documentation).generate(sink, f, context))
return false;
diff --git a/src/bin/eolian_mono/eolian/mono/function_definition.hh b/src/bin/eolian_mono/eolian/mono/function_definition.hh
index 81e2ad81ef..51da954d44 100644
--- a/src/bin/eolian_mono/eolian/mono/function_definition.hh
+++ b/src/bin/eolian_mono/eolian/mono/function_definition.hh
@@ -81,6 +81,10 @@ struct native_function_definition_generator
.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;
+
// 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))
@@ -303,12 +307,12 @@ struct property_wrapper_definition_generator
if (blacklist::is_property_blacklisted(property, *implementing_klass, context))
return true;
- bool interface = context_find_tag(context).current_wrapper_kind == class_context::interface;
+ 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);
- if (interface && is_static)
+ if (is_interface && is_static)
return true;
auto get_params = property.getter.is_engaged() ? property.getter->parameters.size() : 0;
@@ -389,19 +393,21 @@ struct property_wrapper_definition_generator
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) : "";
- if (interface)
+ 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 ((property.klass.type == attributes::class_type::mixin) ||
- (property.klass.type == attributes::class_type::interface_))
- {
- get_scope = "";
- set_scope = "";
- }
else if ((get_scope != "") && (get_scope == set_scope))
{
scope = get_scope;
@@ -439,11 +445,12 @@ struct property_wrapper_definition_generator
return false;
}
- if (property.getter.is_engaged() && interface)
+ if (property.getter.is_engaged() && is_interface)
{
- if (!as_generator(scope_tab << scope_tab << set_scope << "get;\n"
- ).generate(sink, attributes::unused, context))
- return false;
+ 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()*/)
{
@@ -483,11 +490,12 @@ struct property_wrapper_definition_generator
// return false;
// }
- if (property.setter.is_engaged() && interface)
+ if (property.setter.is_engaged() && is_interface)
{
- if (!as_generator(scope_tab << scope_tab << set_scope << "set;\n"
- ).generate(sink, attributes::unused, context))
- return false;
+ 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())
{
diff --git a/src/bin/eolian_mono/eolian/mono/function_helpers.hh b/src/bin/eolian_mono/eolian/mono/function_helpers.hh
index 0ea25da536..5f7cf67877 100644
--- a/src/bin/eolian_mono/eolian/mono/function_helpers.hh
+++ b/src/bin/eolian_mono/eolian/mono/function_helpers.hh
@@ -165,10 +165,6 @@ function_definition_epilogue_generator const as_generator(function_definition_ep
inline std::string function_scope_get(attributes::function_def const& f)
{
- if ((f.klass.type == attributes::class_type::mixin) ||
- (f.klass.type == attributes::class_type::interface_))
- return "public ";
-
switch (f.scope)
{
case attributes::member_scope::scope_public:
@@ -178,7 +174,8 @@ inline std::string function_scope_get(attributes::function_def const& f)
case attributes::member_scope::scope_protected:
return "protected ";
case attributes::member_scope::scope_unknown:
- return " ";
+ // This should trigger a compilation error
+ return "unkown_scope ";
}
return " ";
}
diff --git a/src/bin/eolian_mono/eolian/mono/function_registration.hh b/src/bin/eolian_mono/eolian/mono/function_registration.hh
index b490808b9a..e258ce7cff 100644
--- a/src/bin/eolian_mono/eolian/mono/function_registration.hh
+++ b/src/bin/eolian_mono/eolian/mono/function_registration.hh
@@ -35,6 +35,11 @@ struct function_registration_generator
if(blacklist::is_function_blacklisted(f, context) || f.is_static) // Static methods aren't overrideable
return true;
+ // We do not generate registration wrappers for non public interface/mixin members in their concrete classes.
+ // They go in the first concrete/abstract implementation.
+ if(blacklist::is_non_public_interface_member(f, *klass))
+ return true;
+
if(!as_generator(
indent << "if (" << f.c_name << "_static_delegate == null)\n"
<< indent << "{\n"
diff --git a/src/bin/eolian_mono/eolian/mono/helpers.hh b/src/bin/eolian_mono/eolian/mono/helpers.hh
index a7c27c4809..291e5234ff 100644
--- a/src/bin/eolian_mono/eolian/mono/helpers.hh
+++ b/src/bin/eolian_mono/eolian/mono/helpers.hh
@@ -233,6 +233,39 @@ std::vector get_all_implementable_methods(attributes::
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.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
*/
diff --git a/src/bin/eolian_mono/eolian/mono/klass.hh b/src/bin/eolian_mono/eolian/mono/klass.hh
index 81d1070aab..80ac37c141 100644
--- a/src/bin/eolian_mono/eolian/mono/klass.hh
+++ b/src/bin/eolian_mono/eolian/mono/klass.hh
@@ -31,18 +31,6 @@
namespace eolian_mono {
-/* Get the actual number of functions of a class, checking for blacklisted ones */
-template
-static std::size_t
-get_implementable_function_count(grammar::attributes::klass_def const& cls, Context context)
-{
- auto methods = helpers::get_all_implementable_methods(cls, context);
- return std::count_if(methods.cbegin(), methods.cend(), [&context](grammar::attributes::function_def const& func)
- {
- return !blacklist::is_function_blacklisted(func, context) && !func.is_static;
- });
-}
-
template
static bool
is_inherit_context(Context const& context)
@@ -239,6 +227,9 @@ struct klass
if (!generate_events(sink, cls, concrete_cxt))
return false;
+ if (!as_generator(lit("#pragma warning disable CS0628\n")).generate(sink, attributes::unused, concrete_cxt))
+ return false;
+
// Parts
if(!as_generator(*(part_definition))
.generate(sink, cls.parts, concrete_cxt)) return false;
@@ -263,6 +254,9 @@ struct klass
return false;
}
+ if (!as_generator(lit("#pragma warning restore CS0628\n")).generate(sink, attributes::unused, concrete_cxt))
+ return false;
+
// Copied from nativeinherit class, used when setting up providers.
if(!as_generator(
scope_tab << "private static IntPtr GetEflClassStatic()\n"
@@ -398,7 +392,7 @@ struct klass
context);
auto native_inherit_name = name_helpers::klass_native_inherit_name(cls);
auto inherit_name = name_helpers::klass_inherit_name(cls);
- auto implementable_methods = cls.functions;
+ auto implementable_methods = helpers::get_all_registerable_methods(cls, context);
bool root = !helpers::has_regular_ancestor(cls);
auto const& indent = current_indentation(inative_cxt);
diff --git a/src/bindings/mono/efl_mono/Bind.cs b/src/bindings/mono/efl_mono/Bind.cs
index d82d0c1f49..1d29c25d42 100644
--- a/src/bindings/mono/efl_mono/Bind.cs
+++ b/src/bindings/mono/efl_mono/Bind.cs
@@ -3,6 +3,7 @@
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
+using System.Reflection;
using System.Linq;
using System.ComponentModel;
@@ -47,8 +48,15 @@ public class BindableProperty
throw new InvalidOperationException($"Failed to cast binder {binder} to IPart");
}
- var partBinder = partHolder.GetPart(this.partName) as Efl.Ui.IPropertyBind;
+ // We rely on reflection as GetPart is protected and not generated in IPart.
+ var partMethod = partHolder.GetType().GetMethod("GetPart", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+ if (partMethod == null)
+ {
+ throw new InvalidOperationException($"Failed to get 'GetPart' method on property binder");
+ }
+
+ var partBinder = partMethod.Invoke(partHolder, new System.Object[] { this.partName }) as Efl.Ui.IPropertyBind;
if (partBinder != null)
{
return partBinder.PropertyBind(this.propertyName, modelProperty);
diff --git a/src/lib/eolian_cxx/grammar/klass_def.hpp b/src/lib/eolian_cxx/grammar/klass_def.hpp
index 108be02f79..683bb000a4 100644
--- a/src/lib/eolian_cxx/grammar/klass_def.hpp
+++ b/src/lib/eolian_cxx/grammar/klass_def.hpp
@@ -1277,6 +1277,26 @@ struct klass_def
|| lhs.parts < rhs.parts;
}
+ friend inline bool operator==(klass_def const& lhs, klass_name const& rhs)
+ {
+ return lhs.namespaces == rhs.namespaces
+ && lhs.eolian_name == rhs.eolian_name
+ && lhs.type == rhs.type;
+ }
+ friend inline bool operator==(klass_name const& lhs, klass_def const& rhs)
+ {
+ return rhs == lhs;
+ }
+ friend inline bool operator!=(klass_def const& lhs, klass_name const& rhs)
+ {
+ return !(lhs == rhs);
+ }
+ friend inline bool operator!=(klass_name const& lhs, klass_def const& rhs)
+ {
+ return !(rhs == lhs);
+ }
+
+
klass_def(std::string eolian_name, std::string cxx_name, std::string filename
, documentation_def documentation
, std::vector namespaces
diff --git a/src/tests/efl_mono/Eo.cs b/src/tests/efl_mono/Eo.cs
index 56813b3f87..efb1faa306 100644
--- a/src/tests/efl_mono/Eo.cs
+++ b/src/tests/efl_mono/Eo.cs
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Collections.Generic;
+using System.Reflection;
namespace TestSuite
{
@@ -533,4 +534,77 @@ class TestObjectDeletion
}
}
+class TestProtectedInterfaceMembers
+{
+
+ private class MyObject : Dummy.TestObject
+ {
+ public MyObject(Efl.Object parent = null) : base(parent)
+ {
+ }
+
+ protected override int MethodProtected(int x)
+ {
+ return x * x;
+ }
+ }
+
+ public static void test_protected_interface_in_generated_class_called_from_c()
+ {
+ var obj = new Dummy.TestObject();
+ Test.AssertEquals(obj.CallMethodProtected(42), -42);
+ }
+
+ public static void test_protected_interface_in_inherited_class_called_from_c()
+ {
+ var obj = new MyObject();
+ Test.AssertEquals(obj.CallMethodProtected(42), 42 * 42);
+ }
+
+ public static void test_interface_skipped_protected()
+ {
+ var type = typeof(Dummy.ITestIface);
+ var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance);
+
+ // Fully protected property
+ Test.AssertNull(methods.SingleOrDefault(m => m.Name == "GetProtectedProp"));
+ Test.AssertNull(methods.SingleOrDefault(m => m.Name == "SetProtectedProp"));
+
+ // Partially protected property
+ Test.AssertNotNull(methods.SingleOrDefault(m => m.Name == "GetPublicGetterPrivateSetter"));
+ Test.AssertNull(methods.SingleOrDefault(m => m.Name == "SetPublicGetterPrivateSetter"));
+
+ var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
+ Test.AssertNull(properties.SingleOrDefault(m => m.Name == "ProtectedProp"));
+ Test.AssertNotNull(properties.SingleOrDefault(m => m.Name == "PublicGetterPrivateSetter"));
+ }
+
+ public static void test_interface_skipped_protected_in_implementation()
+ {
+ var type = typeof(Dummy.TestObject);
+
+ // Fully protected property
+ var protected_methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(m => m.IsFamily);
+ Test.AssertNotNull(protected_methods.SingleOrDefault(m => m.Name == "GetProtectedProp"));
+ Test.AssertNotNull(protected_methods.SingleOrDefault(m => m.Name == "SetProtectedProp"));
+
+ // Partially protected property
+ var public_methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance);
+ Test.AssertNotNull(public_methods.SingleOrDefault(m => m.Name == "GetPublicGetterPrivateSetter"));
+ Test.AssertNotNull(protected_methods.SingleOrDefault(m => m.Name == "SetPublicGetterPrivateSetter"));
+
+ var protected_properties = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
+ var prop = protected_properties.SingleOrDefault(m => m.Name == "ProtectedProp");
+ Test.AssertNotNull(prop);
+ Test.Assert(prop.GetMethod.IsFamily);
+ Test.Assert(prop.SetMethod.IsFamily);
+
+ var public_properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
+ prop = public_properties.SingleOrDefault(m => m.Name == "PublicGetterPrivateSetter");
+ Test.AssertNotNull(prop);
+ Test.Assert(prop.GetMethod.IsPublic);
+ Test.Assert(prop.SetMethod.IsFamily);
+ }
+}
+
}
diff --git a/src/tests/efl_mono/dummy_interfaces.c b/src/tests/efl_mono/dummy_interfaces.c
index f4b3ca5948..c60ec59f0a 100644
--- a/src/tests/efl_mono/dummy_interfaces.c
+++ b/src/tests/efl_mono/dummy_interfaces.c
@@ -1,4 +1,6 @@
// Include file for interfaces .eo.c files
+#define DUMMY_TEST_IFACE_PROTECTED
+
#include "libefl_mono_native_test.h"
#include "dummy_test_iface.eo.c"
diff --git a/src/tests/efl_mono/dummy_test_iface.eo b/src/tests/efl_mono/dummy_test_iface.eo
index 3ab37f5407..068b1352bd 100644
--- a/src/tests/efl_mono/dummy_test_iface.eo
+++ b/src/tests/efl_mono/dummy_test_iface.eo
@@ -10,6 +10,37 @@ interface Dummy.Test_Iface
data: int;
}
}
+
+ method_protected @protected @const {
+ params {
+ @in x: int;
+ }
+ return: int;
+ }
+
+ call_method_protected @const {
+ params {
+ @in x: int;
+ }
+ return: int;
+ }
+
+ @property protected_prop @protected {
+ get {}
+ set {}
+ values {
+ data: int;
+ }
+ }
+
+ @property public_getter_private_setter {
+ get {}
+ set @protected {}
+ values {
+ data: int;
+ }
+ }
+
}
events {
nonconflicted: void;
diff --git a/src/tests/efl_mono/dummy_test_object.c b/src/tests/efl_mono/dummy_test_object.c
index 6c32f80ca1..a880dc40c6 100644
--- a/src/tests/efl_mono/dummy_test_object.c
+++ b/src/tests/efl_mono/dummy_test_object.c
@@ -1,4 +1,6 @@
+#define DUMMY_TEST_IFACE_PROTECTED
+
#include "libefl_mono_native_test.h"
typedef struct Dummy_Test_Object_Data
@@ -4704,6 +4706,16 @@ const Eina_Value_Type *_dummy_test_object_mirror_value_type(EINA_UNUSED const Eo
return type;
}
+int _dummy_test_object_dummy_test_iface_method_protected(EINA_UNUSED const Eo *obj, EINA_UNUSED Dummy_Test_Object_Data *pd, int x)
+{
+ return -x;
+}
+
+int _dummy_test_object_dummy_test_iface_call_method_protected(const Eo *obj, EINA_UNUSED Dummy_Test_Object_Data *pd, int x)
+{
+ return dummy_test_iface_method_protected(obj, x);
+}
+
// Inherit
int _dummy_inherit_helper_receive_dummy_and_call_int_out(Dummy_Test_Object *x)
{
diff --git a/src/tests/efl_mono/dummy_test_object.eo b/src/tests/efl_mono/dummy_test_object.eo
index 2fa2c32c55..676b68a421 100644
--- a/src/tests/efl_mono/dummy_test_object.eo
+++ b/src/tests/efl_mono/dummy_test_object.eo
@@ -1653,6 +1653,8 @@ class Dummy.Test_Object extends Efl.Object implements Dummy.Test_Iface {
Efl.Object.provider_find;
Dummy.Test_Iface.emit_nonconflicted;
Dummy.Test_Iface.iface_prop { get; set; }
+ Dummy.Test_Iface.method_protected;
+ Dummy.Test_Iface.call_method_protected;
}
events {
evt,with,string @hot: const(string);