Bug 37695 - Delegate references an overridden method in derived class misbehaves when marshalled as a parameter to native C function.
Summary: Delegate references an overridden method in derived class misbehaves when mar...
Status: RESOLVED FIXED
Alias: None
Product: Runtime
Classification: Mono
Component: JIT (show other bugs)
Version: 4.2.0 (C6)
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2016-01-14 15:40 UTC by Assem Radaideh
Modified: 2016-01-18 16:43 UTC (History)
5 users (show)

See Also:
Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
C file + C# file + shell script to build Native library and the Client Managed Executable (2.17 KB, application/x-zip-compressed)
2016-01-14 15:40 UTC, Assem Radaideh
Details
This is the correct Sample. Use this attachement not the previous (3.24 KB, application/zip)
2016-01-18 07:09 UTC, Assem Radaideh
Details

Description Assem Radaideh 2016-01-14 15:40:31 UTC
Created attachment 14588 [details]
C file + C# file + shell script to build Native library and the Client Managed Executable

Assume we have a delegate as a member in a base class that references an overridden method in a derived class of this base class.

Also, C function takes a function pointer that is equivalent to the managed Delegate's signature as a parameter.

if we PInvoked this function passing that delegate, and tried to "callback" it from within the C function, it should invoke the overridden method in derived class.

I've written a minimal code in C and C# that shows this issue:

Native C Library:

main.c:
//////////////////////////////////////////////////////////////////////////////////////////////////
typedef int (* Virtual_OnInit) ();


#define EXPORT __attribute__((visibility("default")))
#define	FORCE_INLINE __attribute__((always_inline))


EXPORT void RegisterVirtualCallBack(Virtual_OnInit onInit)
{

	printf("NATIVE: RegisterVirtualCallBack Entered\n");

	onInit(); // invoke the passed from managed code delegate

	printf("NATIVE: RegisterVirtualCallBack Left\n");


}
//////////////////////////////////////////////////////////////////////////////////////////////////


Build the library using the follwoing:
>gcc -MMD ./main.c -fPIC -O3 -arch i386 -shared -o "./libTestCallBacksPInvoke.so"







and the C# file:
Program.cs:

//////////////////////////////////////////////////////////////////////////////////////////////////
using System;
using System.Runtime.InteropServices;

namespace TestDelegatesPInvoke
{


    class Base
    {
        public delegate void Virtual_OnInit();
        private Virtual_OnInit virtual_OnInit;


        [DllImport("TestCallBacksPInvoke")]
        public static extern void RegisterVirtualCallBack(Virtual_OnInit delegateOnInit);


        public Base()
        {
            Console.WriteLine("This is Base's Constructor");

            virtual_OnInit = new Virtual_OnInit(OnInit);

            RegisterVirtualCallBack(virtual_OnInit);
        }
            

        public virtual void OnInit()
        {
            Console.WriteLine("MANAGED: This is Base's Virtual OnInit");
        }
    }


    class Derived : Base
    {

        public Derived()
        {
            Console.WriteLine("This is Derived's Constructor");
        }
            
        public override void OnInit()
        {
            Console.WriteLine("MANAGED: This is Derived's OnInit");
        }
    }
        



    class MainClass
    {
        public static void Main(string[] args)
        {
            
            Base b = new Derived(); // the virtual_OnInit delegate in the base references Derived's OnInit
           
        }
    }
}
//////////////////////////////////////////////////////////////////////////////////////////////////






To build the exe use:
>mcs /noconfig /debug:full /debug+ /optimize- /out:TestCallback.exe Program.cs /target:exe /define:DEBUG /nostdlib /reference:/Library/Frameworks/Mono.framework/Versions/4.2.1/lib/mono/4.5/System.dll /reference:/Library/Frameworks/Mono.framework/Versions/4.2.1/lib/mono/4.5/System.Core.dll /reference:/Library/Frameworks/Mono.framework/Versions/4.2.1/lib/mono/4.5/mscorlib.dll /warn:4



### When the C# code above built and executed with Mono 4.2.1, the console output is ###

This is Base's Constructor
NATIVE: RegisterVirtualCallBack Entered
MANAGED: This is Base's Virtual OnInit
NATIVE: RegisterVirtualCallBack Left
This is Derived's Constructor



### When built and executed with Mono 4.0.5 and below, it produces the following ###

This is Base's Constructor
NATIVE: RegisterVirtualCallBack Entered
MANAGED: This is Derived's OnInit
NATIVE: RegisterVirtualCallBack Left
This is Derived's Constructor


Which is the correct/needed behavior I believe.
Comment 1 Assem Radaideh 2016-01-18 07:09:52 UTC
Created attachment 14614 [details]
This is the correct Sample. Use this attachement not the previous

This zip file contains 3 files:

1- C file exporting a function to be built as an Shared library (.so)
2- C# file having a class that PInvokes the exported function, passing a delegate that referenced an overridden method.

3- simple shell script file that build both the Native library + .Net Console Executable.

to see the difference : build and run that executable using mono 4.0.5 or bellow and then with 4.2.1 or above. and compare the written 3rd line for both outputs.
Comment 2 Zoltan Varga 2016-01-18 16:43:36 UTC
Fixed in mono master 33104f6a78e23fe8cba86c935a254b6e548fc53e.

Note You need to log in before you can comment on or make changes to this bug.