Page MenuHomePhabricator

Discussion about C# binding syntax
Open, HighPublic

Description

C# class generation problem:

Classes generated in C# do not automatically get called when the same method gets called through Eo.

So, when the generator generates a class efl.ui.Button, and EFL itself calls a move method on the Button, the move method in C# do not get this call. Which means that C# never gets to know that this method was ever called and so if a class inherits from Button, that class's move override do not get called too.

The class is a one-way communication between C# -> C.

The way to allow a bidirectional communication is for the C# side to create a new class and override all Ops of the overridden class, and when that gets called try to call the most override of that class, with a base default that calls with efl_super, by passing the overridden Eo class.

This means that for each call made to the object, a C# method gets called, independently if that method was overridden or not. And if it was not, calls back through Eo to the implementation from parent class.

This is what is currently the class efl.ui.ButtonInherit for the efl.ui.Button in C#.

My proposal is to have just one concrete, implemented class, that when is overridden creates a new class and does the intermediary to allow override from C#, and if it is not inherited from, does just normal calls.

This, OTH, requires that multiple inheritance from classes is not possible from Eolian. The classes would generate just one single class (and no interface) that would work both ways and interfaces and mixins would generate a class and an interface. The class is necessary because when a method returns an interface, the C# binding must return a class that implements said interface and routes the calls through Eo.

This also restricts creating classes in C# that interoperate with Eolian that do not inherit from a proper Eolian class. All C# classes that will be passed to EFL code will have to inherit from a class, they can't inherit just from interfaces. This follows the idea that all EFL classes inherit, directly or indirectly, from Efl.Object. But this rule is not mandatory for other languages.

Let me know if you have any questions about this proposal.

felipealmeida triaged this task as High priority.
segfaultxavi edited subscribers, added: segfaultxavi; removed: xavi.

I am going to use this ticket as a base for the C# documentation. @felipealmeida could you edit the description to add an explanation of the three C# classes that can be built for each eo class? (Button, IButton and ButtonInherit).

Also, it would be extremely helpful if you included a short example of how to:

  • instantiate EFL classes from C#
  • call C# methods passing and returning Eo classes
  • inherit from an EFL class

All the above, for the current state and for your proposed changed.

I am sorry to ask for all this, but I haven't found it anywhere else.

zmike added a project: Restricted Project.Jul 24 2018, 10:45 AM

So, actually there is quite a few multiple inheritance:

src/lib/edje/efl_canvas_layout_part.eo
src/lib/edje/efl_canvas_layout_part_external.eo
src/lib/elementary/efl_ui_image_zoomable.eo
src/lib/evas/canvas/efl_canvas_object.eo
src/lib/elementary/efl_ui_navigation_bar_part_back_button.eo
src/lib/elementary/efl_ui_win.eo.cs
src/lib/elementary/efl_ui_popup_part.eo.cs
src/lib/elementary/efl_ui_popup_alert_scroll_part.eo.cs
src/lib/elementary/efl_ui_popup_alert_text_part.eo.cs
src/lib/elementary/efl_ui_textpath.eo.cs

I've pushed the code that generates these errors in devs/felipealmeida/csharp-new-classes

It is not finished, it needs a lot of work on the new Inherit classes (which become now the Concrete classes). However, since there are so many multiple inheritance, it is impossible to compile mono classes with the current inheritance tree.

@felipealmeida

I have some reports and questions about the current EFL C# bindings as follows.

<Reports>

  1. Crash with ClickedEt callback
    • When I register callback function for ClickedEvt, crash happens when clicked event happens. (e.g. btn.ClickedEvt += callback;)
  1. ClickedEvt cannot be found in the user-defined class which inherits from efl.ui.ButtonInherit class.
    • If I write a code as follows, error happens.
    • To use ClickedEvt, the variable needs to be casted to efl.ui.IButton. I think this is not a correct way though.
class MyButton : efl.ui.ButtonInherit
{
   ...
}

class Test
{
   public static void Main()
   {
      ...
      MyButton btn = new MyButton(parent);
      btn.ClickedEvt += callback; //This causes error because ClickedEvt cannot be found in MyButton.
      ...
   }
}

<Questions>

  1. What if multi class inheritance is not allowed in eo?
    • As far as I know, the reason, why efl.ui.IButton exists and there is no class hierarchy in generated cs files, is because eo allows multi class inheritance. If so, is it possible to generate class hierarchy in cs files without efl.ui.IButton and does not need to implement all interface methods? (e.g. In efl_ui_button.eo.cs, efl.ui.IButton does not exist and SetSize() method does not need to be implemented because it is implemented in efl.ui.Layout.Object.)
  1. About class only for inheritance
    • You mentioned that we need class for inheritance which effectively creates a new Eo class in Eo virtual call mechanism. Could you explain details why we need to create new eo class instead of existing widget class (e.g. efl_ui_button)?
    • hermet and I checked that the difference between Button and ButtonInherit constructor is as follows. It seems that the new eo class is the same as efl_ui_button class. What happen if user-defined class inherits efl.ui.Button unless it is not sealed class?
klass = efl.eo.Globals.register_class(new efl.eo.Globals.class_initializer(ButtonNativeInherit.class_initializer), "Button", efl_ui_button_class_get());

Hello @Jaehyun_Cho

I have some reports and questions about the current EFL C# bindings as follows.

<Reports>

  1. Crash with ClickedEt callback
    • When I register callback function for ClickedEvt, crash happens when clicked event happens. (e.g. btn.ClickedEvt += callback;)

We'll check it.

  1. ClickedEvt cannot be found in the user-defined class which inherits from efl.ui.ButtonInherit class.
    • If I write a code as follows, error happens.
    • To use ClickedEvt, the variable needs to be casted to efl.ui.IButton. I think this is not a correct way though.

That's why I'm testing a different way to do things.

class MyButton : efl.ui.ButtonInherit
{
   ...
}

class Test
{
   public static void Main()
   {
      ...
      MyButton btn = new MyButton(parent);
      btn.ClickedEvt += callback; //This causes error because ClickedEvt cannot be found in MyButton.
      ...
   }
}

Unfortunately I can't include events to classes defined by the user. But I think the modifications I'm proposing will allow the event to be found in this case.

<Questions>

  1. What if multi class inheritance is not allowed in eo?
    • As far as I know, the reason, why efl.ui.IButton exists and there is no class hierarchy in generated cs files, is because eo allows multi class inheritance. If so, is it possible to generate class hierarchy in cs files without efl.ui.IButton and does not need to implement all interface methods? (e.g. In efl_ui_button.eo.cs, efl.ui.IButton does not exist and SetSize() method does not need to be implemented because it is implemented in efl.ui.Layout.Object.)

There are more reasons, but they are a tradeoff that I think it is not worth doing that way. The separation was to allow multiple inheritance in Eolian and
give a consistent generation for interfaces and classes. Interfaces need implementations too in C#.

  1. About class only for inheritance
    • You mentioned that we need class for inheritance which effectively creates a new Eo class in Eo virtual call mechanism. Could you explain details why we need to create new eo class instead of existing widget class (e.g. efl_ui_button)?

Well, we're modifying to make that the same class, which will work differently based on if the current instance is of a inherited class or not. The difference between the two cases is that the implementation for both are a "little" different and how they are called by Eo is _completely_ different.

The inherit class creates a new class in the constructor and registers all its methods as the methods to be called, instead of their bases and interfaces. This is done _even_ if no method was overriden by the user, because the class doesn't know which methods was overriden or not. So, what happens when Eo calls a method on this new class through its base is that the C# gets called to a trampoline method that calls its own method, if the method was overriden then that method is called, otherwise the default implementation is called which just calls the same method with efl_super (so it doesn't call itself again).

The normal concrete class, OTOH, doesn't do anything like that, so when a call in C is made by Eo, the call goes directly to efl_ui_button.c implementation and _not_ to C#. So, inheriting from it doesn't work because the call never gets routed to C# and C# is not even aware that a call was made, because it was made by Eo from C, which C# doesn't have any control.

  • hermet and I checked that the difference between Button and ButtonInherit constructor is as follows. It seems that the new eo class is the same as efl_ui_button class.

It is not. It is a new class that inherits from efl_ui_button_class_get and has as class_initializer the static method ButtonNativeInherit.class_initializer which initializes the Op array with trampoline methods to call itself.

What happen if user-defined class inherits efl.ui.Button unless it is not sealed class?

The sealing is there just so user doesn't inherit from it thinking that when he overrides the methods that he will get called, which will only happen if the call is made through C#. However, I'll merge the two classes and make the behavior consistent with however case it is.

klass = efl.eo.Globals.register_class(new efl.eo.Globals.class_initializer(ButtonNativeInherit.class_initializer), "Button", efl_ui_button_class_get());

Regards,

@felipealmeida Thank you for replying.

We'll check it.

That's why I'm testing a different way to do things.

Please share us when you fix the issue reports.

There are more reasons, but they are a tradeoff that I think it is not worth doing that way. The separation was to allow multiple inheritance in Eolian and
give a consistent generation for interfaces and classes. Interfaces need implementations too in C#.

I've made a task T7240 to discuss not to allow multi class inheritance. So let's think about it in that task.

Well, we're modifying to make that the same class, which will work differently based on if the current instance is of a inherited class or not. The difference between the two cases is that the implementation for both are a "little" different > and how they are called by Eo is _completely_ different.

The sealing is there just so user doesn't inherit from it thinking that when he overrides the methods that he will get called, which will only happen if the call is made through C#. However, I'll merge the two classes and make the behavior > consistent with however case it is.

Thank you for making normal class and inherit class into one class. (i.e. Button and ButtonInherit into Button class)
Please share us when you have result or any progress.

Best Regards,

Hello @felipealmeida and @lauromoura

I have some questions.

  1. How to get evas from evas_object
    • As far as I see, the only way to get evas from efl canvas object is by importing evas_object_evas_get() as follows.
[DllImport("evas")] static extern IntPtr evas_object_evas_get(IntPtr obj);
IntPtr evas = evas_object_evas_get(win.raw_handle);
    • Is there any other way to get evas without import evas_object_evas_get()?
    • How can I use efl.ui.IWin win; win.FindProvider()? (I cannot find efl.IObject klass)
    • How about providing evas_object_main's APIs (e.g. evas_object_evas_get) in efl/src/bindings/mono/? (e.g. evas_mono)
  1. Fixed size of ecore_evas_new() in ecore_evas.cs
    • When EcoreEvas class instance is created, the canvas size is fixed with 640 x 480 and there is no way to change it.
    • What if overloaded constructor has size parameters?

Thank you

Hello @Jaehyun_Cho ,

It was seen at the time that Evas_Core was not supposed (only Evas Canvas) to be used by bindings and be an implementation detail only. I think this was true because elementary was to be seen as the high-level API. Since implementers will be using Evas directly, then this may need to change. Shouldn't Evas be Eolized then?

Regards,

zmike edited projects, added Active Work Proposal; removed Voting.Aug 20 2018, 4:55 AM

Hi @felipealmeida!

Any progress on this? I am unsure if you need anything from us or you are making progress on your own.

Also, it would really help me if you could answers my questions from July 24th. I am just asking for very short snippets of C# code... ๐Ÿ˜

It would be extremely helpful if you included a short example of how to:

  • instantiate EFL classes from C#
  • call C# methods passing and returning EFL classes
  • inherit from an EFL class

All the above, for the current state and for your proposed changes.

Thanks!

Hi @felipealmeida!

Any progress on this? I am unsure if you need anything from us or you are making progress on your own.

Felipe is at HQ this week. I'm working on getting the new proposal - removing the explicit *Inherit stuff - working. Some tests are still failing, but I'll try to push to a branch later today.

Also, it would really help me if you could answers my questions from July 24th. I am just asking for very short snippets of C# code... ๐Ÿ˜

It would be extremely helpful if you included a short example of how to:

  • instantiate EFL classes from C#

So far, all constructors have the same signature, e.g. efl.ui.Button:

public efl.ui.Button(efl.Object parent=null, efl.ui.Button.ConstructingMethod init_cb=null)

ConstructingMethod is a delegate (something like a function pointer) that will be called inside the constructor to allow calling constructing methods between C's efl_add_start and efl_add_end. Something like calling those constructor functions with efl_added in C.

public delegate void ConstructingMethod(efl.ui.Button button);

So, a more complete example:

public void ButtonInitCb(efl.ui.Button button) {
    button.SetText("Click me!");
}
...
var button = new efl.ui.Button(some_parent, ButtonInitCb);
  • call C# methods passing and returning EFL classes

Do you mean passing normal EFL classes instances or some kind of passing proper classes like first-class items (like a factory method in python receiving classes...)?

This part haven't changed much in this refactor. The main change is that the interface for concrete Eo classes disappeared, as they are now proper C# classes only (Interfaces and Mixins are still C# interfaces, though)

  • inherit from an EFL class
public class MyButton : efl.ui.Button {

    // Using a C# lambda as constructing method to set the button text during construction
    public MyButton(String text, efl.Object parent=null) : base(parent, (efl.ui.Button self) => { SetText(text); } {
    }
}

All the above, for the current state and for your proposed changes.

Thanks!

Sorry for the delay. Anything just ask :)

Thanks a lot Lauro for the very detailed answer. I still have some questions though.

  • Regarding class instantiation, I wanted to clarify the type of the variable (unfortunately you used var in your example). So, is the following code still the recommended way? efl.ui.IBox box = new efl.ui.Box(win);
  • Does the above still work after the refactor?
  • Regarding the passing of EFL objects as parameters, again, I wanted to clarify the type to use (efl.ui.Box, efl.ui.IBox, or something else).
  • Does the above still work after the refactor?
  • Regarding the inheritance from EFL C# classes, your example shows the correct way after the refactor, correct? That is not how we can do it right now, I think.
  • What is the efl.All namespace? Why is it efl.All.Init() instead of efl.Init() which seems simpler?
  • What is the efl.ui.Config namespace? Why is it efl.ui.Config.Run() and efl.ui.Config.Exit() instead of efl.Run() and efl.Exit()?

Can you answer the above questions one by one? I am sorry but I still do not understand all the intended changes for this refactor, and I'll have to document them :)
Thanks a lot!

Thanks a lot Lauro for the very detailed answer. I still have some questions though.

  • Regarding class instantiation, I wanted to clarify the type of the variable (unfortunately you used var in your example). So, is the following code still the recommended way? efl.ui.IBox box = new efl.ui.Box(win);

var works like auto in C++, just a shortcut to avoid typing the type names twice. In this case, the type of button would be efl.ui.Button.

  • Does the above still work after the refactor?

Before the refactor the correct way is to always use the interface (e.g. efl.ui.IButton) for variables. The concrete class should be used only to call the constructor and call static methods (C# interfaces can't have static methods).

So:

  • efl.ui.IButton - Interface, should be the 'public' face of the class, used in variables, arguments, etc.
  • efl.ui.Button- Concrete implementation, when you don't want to inherit from it, just instantiate or call static methods.
  • efl.ui.ButtonInherit - Class to be inherited from.

After the refactor, all Eo classes should have a single class wrapping them (e.g. efl.ui.Button, "absorbing" the Inherit class), to avoid this IFoo/Foo/FooInherit confusion.

  • Regarding the passing of EFL objects as parameters, again, I wanted to clarify the type to use (efl.ui.Box, efl.ui.IBox, or something else).
  • Does the above still work after the refactor?

Ditto as above (before refactor, use interfaces for variables)

  • Regarding the inheritance from EFL C# classes, your example shows the correct way after the refactor, correct? That is not how we can do it right now, I think.

Right now you have to use the inherit classes (e.g. efl.ui.ButtonInherit) as the base class. The constructors receive the same arguments.

  • What is the efl.All namespace? Why is it efl.All.Init() instead of efl.Init() which seems simpler?

C# namespaces can't have top-level functions, so we created this All class to hold these free functions. I'd like to have efl.Config.Init() instead of efl.All.Init() but the former was already claimed by an Eo interface.

  • What is the efl.ui.Config namespace? Why is it efl.ui.Config.Run() and efl.ui.Config.Exit() instead of efl.Run() and efl.Exit()?

For the same reason as efl.All, in the other modules (Eo, Ecore, Elementary, etc), we have the Config class to hold the individual init/shutdown functions.

This init/shutdown scheme shouldn't change in the refactor.

Can you answer the above questions one by one? I am sorry but I still do not understand all the intended changes for this refactor, and I'll have to document them :)
Thanks a lot!

No problem.

OK, understood, thanks.

I also understand now the need for the efl.All and efl.ui.Config artificial classes. However, since they serve the same purpose (C# does not support global methods), I really think they should have the same name. As you say, Config is already an Eo interface, so what about efl.Global and efl.ui.Global ?

We could save a lot of questions and answers if you made an internal doc about all the C# bindings structure (and then I could use that to write the public docs) ๐Ÿ˜œ

@lauromoura Why are there init / shutdown functions at all? Isn't there a way to automatically init elementary when mono is spinning up ?

OK, understood, thanks.

I also understand now the need for the efl.All and efl.ui.Config artificial classes. However, since they serve the same purpose (C# does not support global methods), I really think they should have the same name. As you say, Config is already an Eo interface, so what about efl.Global and efl.ui.Global ?

A common name would be nice. But there's an Edge.Global already... :)

What about efl.Setup.Init(), eina.Init(), etc?

We could save a lot of questions and answers if you made an internal doc about all the C# bindings structure (and then I could use that to write the public docs) ๐Ÿ˜œ

After this revamp it will be stable enough to do something like that.

lauromoura added a comment.EditedAug 21 2018, 2:55 PM

@lauromoura Why are there init / shutdown functions at all? Isn't there a way to automatically init elementary when mono is spinning up ?

I couldn't find anything of the kind other than Windows-specific stuff like OnPaint, etc.

What if we hid those functions inside an Efl.Initialize(EntryFunc func) function to work somewhat like efl_main?

Something like (edited below with init/shutdown comments):

Binding code:

namespace Efl {
public static class All
{
    public delegate void MainFunction(string[] args=null);
    public static void Initialize(MainFunction main)
    {
        Console.WriteLine("Entered EFL Main");
        // foo_init();
        main();
        // foo_shutdown();
        Console.WriteLine("Exited EFL Main");
    }
}
}

Binding user code:

using System;

internal static class Efl_Main
{
    internal static void Entry(string[] args)
    {
        Console.WriteLine("Started client code.");
    }

    public static void Main(string[] args)
    {
        Efl.All.Initialize(Entry);
    }
}

(edit: fixed more typos and added namespace)

Another alternative would be hiding the init into the constructors/class methods, initializing the libraries lazily, at the cost of a bool check on each call for these functions.

@lauromoura i like that idea a lot! (Your first proposal)

Maybe take a look on what EFL_LOOP_EVENT_ARGUMENTS does, this could be similar :)

I would also very much like to be able to access EFL_LOOP_EVENT_ARGUMENTS, so all objects (including the main window) can be a child of the app's main loop, without having to use efl_main_loop_get().

Jaehyun_Cho added a comment.EditedAug 28 2018, 8:00 PM

@lauromoura @felipealmeida

Could you tell me what branch do you use for the latest development of C# bindings?

I want to test class inheritance and override functions based on your latest development branch.
Otherwise, I would try to test and solve issues which are already solved..

e.g. Based on master branch, if I override FinalizeAdd() which calls efl_finalize(), then the overridden function is not called at all. Overridden FinalizeAdd() is not called because FinalizeAdd() is not called when efl_finalize() is called from efl.eo.Globals._efl_add_end().
As far as know, FinalizeAdd() should be called if efl_finalize() is called to support function override in C#.

Thanks :)

@lauromoura @felipealmeida

Could you tell me what branch do you use for the latest development of C# bindings?

I'm still running into some problems regarding the proper way to handle inheritance (and events implementation) in the new refactor - mostly how to mix the elements wrapped with C# interfaces (Eo mixins, interfaces, etc) and C# classes (Eo classes). I hope I can get at least one of the examples running tomorrow (Friday).

I want to test class inheritance and override functions based on your latest development branch.
Otherwise, I would try to test and solve issues which are already solved..

e.g. Based on master branch, if I override FinalizeAdd() which calls efl_finalize(), then the overridden function is not called at all. Overridden FinalizeAdd() is not called because FinalizeAdd() is not called when efl_finalize() is called from efl.eo.Globals._efl_add_end().
As far as know, FinalizeAdd() should be called if efl_finalize() is called to support function override in C#.

Thanks :)

The current Inherit class code called efl_add_end before saving the C# wrapper instance handle into the Eo private data. There is a fix in D6956.

@lauromoura @felipealmeida

Thank you for sharing and fix D6956.

Regarding D6956, still override FinalizeAdd() is not called. (I tested dotnet run with user-defined class which inherits from efl.ui.ButtonInherit.)

The reason is because class_initializer of inherit class in cs file fails to load some efl functions from dlsym (i.e. descs[].api_func) (e.g. efl_selection_set).
This fails _eo_class_funcs_set in eo.c with error message "Class '%s': NULL API not allowed (NULL->%p '%s').".
The efl functions which cannot be loaded from dlsym are methods of mixin, class, and abstract class in elementary. (e.g. efl_selection_set)

My user-defined class is instantiated after efl.ui.Config.Init() is called, so I think that there should not be any problem to load elementary functions from dlsym in efl_ui_button.eo.cs file.
If you know anything about this problem, please share with us.

Thank you :)

@lauromoura @felipealmeida

Thank you for sharing and fix D6956.

Regarding D6956, still override FinalizeAdd() is not called. (I tested dotnet run with user-defined class which inherits from efl.ui.ButtonInherit.)

The reason is because class_initializer of inherit class in cs file fails to load some efl functions from dlsym (i.e. descs[].api_func) (e.g. efl_selection_set).
This fails _eo_class_funcs_set in eo.c with error message "Class '%s': NULL API not allowed (NULL->%p '%s').".
The efl functions which cannot be loaded from dlsym are methods of mixin, class, and abstract class in elementary. (e.g. efl_selection_set)

My user-defined class is instantiated after efl.ui.Config.Init() is called, so I think that there should not be any problem to load elementary functions from dlsym in efl_ui_button.eo.cs file.
If you know anything about this problem, please share with us.

Thank you :)

It may be something related to the initialization. Are you using efl.ui.Config.Init() directly instead of efl.All.Init(efl.Components.Ui)? I prefer the latter as it already initializes the other modules.

The example below calls finalize (although there are a bunch of Eo errors on exit, until we tackle the gc on exit).

using System;

public class MyButton : efl.ui.ButtonInherit
{

    public MyButton(efl.IObject parent) : base(parent)
    {
    }

    public override efl.IObject FinalizeAdd()
    {
        Console.WriteLine("Called finalize");
        return this;
    }
    public static void Main()
    {
        efl.All.Init(efl.Components.Ui);

        var window = new efl.ui.Win();
        var button = new MyButton(window);

    }
}

@lauromoura

It is strange.. I called efl.All.Init(efl.Components.Ui); when I tested. And it did not work on the latest master branch.

I think maybe you tested on your local branch.

Please let me know your test branch or please test on the latest master branch. (if you test on master branch, then please do not use previously installed so files and header files from your local branch.)

Thank you :)

@lauromoura

It is strange.. I called efl.All.Init(efl.Components.Ui); when I tested. And it did not work on the latest master branch.

I think maybe you tested on your local branch.

Please let me know your test branch or please test on the latest master branch. (if you test on master branch, then please do not use previously installed so files and header files from your local branch.)

Thank you :)

Here's the steps I took:

  • Cleaned up build tree and destdir
  • Checked out master (423d8a22961436299df0feca17b03544678b8c0f as of now)
  • Applied the proposed patch (D6956)
  • Built, installed
  • Compiled the example above with meson (which just passes the -r flags to mcs)

Could you try running the tests to check if the added test case fails for you too?

@lauromoura

Thank you for explanation about how to build with meson.

I built with meson and then all elementary functions are successfully loaded by dlsym in class_initializer.
But I still don't know why those functions are not loaded by dlsym with dotnet build...
Our goal is to build EFL C# on Visual Studio so it still matters..

Please let me know if you build successfully with dotnet build.

We use csproj file as follows.

<Project Sdk="Microsoft.NET.Sdk">                                                
                                                                                 
  <PropertyGroup>                                                                
    <OutputType>Exe</OutputType>                                                 
    <TargetFramework>netcoreapp2.0</TargetFramework>                             
  </PropertyGroup>                                                               
  <ItemGroup>                                                                    
    <Reference Include="libefl_mono">                                            
      <HintPath>/usr/local/lib/efl-mono-1/libefl_mono.dll</HintPath>
    </Reference>                                                                 
  </ItemGroup>                                                                   
                                                                                 
</Project>

@lauromoura

When I tested with meson, then FinalizeAdd was called successfully but SetText() was failed.

The reason is that additional efl_ref() is called in MarshalNativeToManaged() in iwrapper.cs and it causes that eina safety check fails as follows.

ERR<22264>:eina_safety lib/efl/interfaces/efl_interfaces_main.c:103 efl_part() safety check failed: efl_ref_count(r) == 1 is false

Is there any way to avoid calling efl_ref() in MarshalNativeToManaged()? Otherwise, it causes that safety check fails.

lauromoura added a comment.EditedSep 11 2018, 6:46 PM

@lauromoura

Thank you for explanation about how to build with meson.

I built with meson and then all elementary functions are successfully loaded by dlsym in class_initializer.
But I still don't know why those functions are not loaded by dlsym with dotnet build...
Our goal is to build EFL C# on Visual Studio so it still matters..

Please let me know if you build successfully with dotnet build.

The current class registration code uses dlsym(RTLD_DEFAULT, name) to load the class members, relying on mono's usage of RTLD_GLOBAL by default (function mono_dl_convert_flags() in mono-dl-posix.c inside mono code)

dotnet uses GetProcAddress behavior like RTLD_LOCAL (in CoreCLRLoader::Create() in coreclrloader.cpp), RTLD_LOCAL (dlopen default) thus requiring us to pass the handle of the dlopen'ed library explicitly.

I'm going to create a new task to deal specifically with this change.

PS: T7394

I've pushed the current code to devs/lauromoura/csharp-new-classes.

It assumes Eo interfaces/mixins are becoming C# interfaces with 'hidden' concrete implementations and Eo regular/abstracts are proper C# classes.

To deal with the mixins inheriting from abstract (as in the list in https://phab.enlightenment.org/T7240#124488), the abstract parent are currently "bypassed" from the this inheritance line, with the mixin bypassing the abstract they inherit from, inheriting directly from the "grand-parent" interfaces (parent of the abstract).

It compiles but still have some work to do:

  • Rebase over the current inheritance changes.
    • Some .eo changes are in separate commits in the branch, one of the backported from Jaehyun's changes.
  • Allow accessing events directly
    • Interface events are declared in the interface (e.g. efl.ui.Clicable.ClickedEvt) and implemented "anonymously" into the concrete classes that implement the interface, requiring a cast into the interface to access them (e.g. ((efl.ui.Clickable)mybutton).ClickedEvt += ...)
  • Segfaults when instantiating a window (Didn't dig into it yet)
  • Provide a way to convert mixin's back into their actual implementation (downcasting)
  • Tests fail with a strange bug about mono complaining about passing a class (efl.Object) to a DllImport'd function (efl_unref(IntPtr)) even though we explicitly pass the Eo* IntPtr.
    • Tomorrow I'll test it running the test suite with the dotnet core.

Nice to see that this is progressing! Good work!

Maybe we should create other tasks for all the other things we have also asked ๐Ÿ˜„

  • Implicit EFL Init and Shutdown
  • Retrieving the EFL_LOOP_EVENT_ARGUMENTS