Search Unity

Help with AOT compilation (with MonoPInvokeCallback)

Discussion in 'Editor & General Support' started by junestone, Jul 28, 2019.

  1. junestone

    junestone

    Joined:
    May 30, 2017
    Posts:
    42
    Code (CSharp):
    1.         internal static IntPtr GetThunk(MethodInfo method, string funcType = null)
    2.         {
    3.             Type dt;
    4.             if (funcType != null)
    5.                 dt = typeof(Interop).GetNestedType(funcType) as Type;
    6.             else
    7.                 dt = GetPrototype(method.Name);
    8.             if (dt != null)
    9.             {
    10.                 Delegate d = Delegate.CreateDelegate(dt, method);
    11. #if AOT
    12.                 IntPtr fp = Marshal.GetFunctionPointerForDelegate(d);
    13. #else
    14.                 IntPtr tmp = Marshal.AllocHGlobal(IntPtr.Size);
    15.                 Thunk cb = new Thunk(d);
    16.                 Marshal.StructureToPtr(cb, tmp, false);
    17.                 IntPtr fp = Marshal.ReadIntPtr(tmp, 0);
    18.                 Marshal.FreeHGlobal(tmp);
    19. #endif
    20.                 keepAlive.Add(d);
    21.                 return fp;
    22.             }
    23.             return IntPtr.Zero;
    24.         }
    inside AOT macro:
    IntPtr fp = Marshal.GetFunctionPointerForDelegate(d);
    when run the program this line of code will trough error:
    NotSupportedException: To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition.
    at System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegateInternal (System.Delegate d) [0x00000] in <00000000000000000000000000000000>:0
    at System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate (System.Delegate d) [0x00000] in <00000000000000000000000000000000>:0
    at Python.Runtime.Interop.GetThunk (System.Reflection.MethodInfo method, System.String funcType) [0x00000] in <00000000000000000000000000000000>:0
     
  2. junestone

    junestone

    Joined:
    May 30, 2017
    Posts:
    42
    we get stuck with iOS build.
    Please help, any Unity Technologies staff would be appreciated.
     
  3. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    4,153
    You will need to add an attribute named MonoPInvokeCallback to any C# method that you want to call from native code. This attribute is available in the UntiyEngine.AOT namespace, or you can simply create your won attribute class with that same name.
     
  4. junestone

    junestone

    Joined:
    May 30, 2017
    Posts:
    42
    Yeah, What u said is very straightforward and clear, and we've been doing it for quite a while.
    Code (CSharp):
    1. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    2. internal struct Thunk
    3. {
    4.      public Delegate fn;
    5.  
    6.       public Thunk(Delegate d)
    7.       {
    8.             fn = d;
    9.       }
    10.  }
    11.  
    12. IntPtr CreateAOTCompatibleFP(MethodInfo method, Type dt)
    13. {
    14.     Delegate d = Delegate.CreateDelegate(dt, method);
    15.     IntPtr tmp = Marshal.AllocHGlobal(IntPtr.Size);
    16.     Thunk cb = new Thunk(d); //THIS IS WHERE IL2CPP COMPLAINS
    17.     Marshal.StructureToPtr(cb, tmp, false);
    18.     IntPtr fp = Marshal.ReadIntPtr(tmp, 0);
    19.     Marshal.FreeHGlobal(tmp);
    20.     return fp;
    21. }
    22.  
    23. //The delegatetype we passed in
    24. [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    25. public delegate IntPtr BinaryFunc(IntPtr ob, IntPtr arg);
    26.  
    27. CreateAOTCompatibleFP(instancemethod, typeof(BinaryFunc))
    28.  
    29.  
    Since it's an delegate of instance method, we don't know how to correctly create a static MonoPInvokeCallbackAttribute method.

    A guy who succeed it said it should be done by using Mono.Cecil to inject IL code to the managed method before il2cpp compiles, but we don't know the details.

    This is piece of code from Pythonnet. And UnityTechnology also forked it and made some modifications on it. I think the Unity engineer who forked pythonnet might know how to do it.
     
    Last edited: Jul 29, 2019
  5. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    4,153
    The limitation here is not the missing MonoPInvokeCallback attribute then. Rather, the problem is that this an instance method. It is simple not possible to call from native to managed code when that managed code is an instance method with IL2CPP (or with Mono AOT) for that matter. This requires a JIT runtime to work correctly.
     
  6. junestone

    junestone

    Joined:
    May 30, 2017
    Posts:
    42
    thx for your patience. Since we are lack of insights of compilers, so maybe more information could be more precise.

    Code (CSharp):
    1. class MetaType
    2. {
    3.        public static IntPtr __instancecheck__(IntPtr tp, IntPtr args)
    4.         {
    5.             return DoInstanceCheck(tp, args, false);
    6.         }
    7. }
    8.  
    9. //The final piece of code that calls the method
    10.  
    11. mdef = WriteMethodDef(
    12.         mdef,
    13.         "__instancecheck__",
    14.         Interop.GetAOTCompatibleFP(typeof(MetaType).GetMethod("__instancecheck__"), typeof(BinaryFunc))
    15. );
    If there is any mistake I made, feel free to point it out.
     
  7. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    4,153
    Sorry, I don't understand what this code is attempting to do.
     
  8. junestone

    junestone

    Joined:
    May 30, 2017
    Posts:
    42
    To write a function pointer(Unmanaged) of __instancecheck__ method(Managed) to a Managed PythonMethod object, interactable with CPython runtime.
    Project url: https://github.com/joonhwan/pythonnet
     
  9. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    4,153
    I believe you will need to add the MonoPInvokeCallback attribute to the method named __instancecheck__ then. Although if this method is only access via reflection (as it is in this example), then managed code stripping will likely remove it anyway.

    You may want to have a look at the Unity documentation about managed code stripping here: https://docs.unity3d.com/Manual/ManagedCodeStripping.html
     
  10. junestone

    junestone

    Joined:
    May 30, 2017
    Posts:
    42
    Code (CSharp):
    1. typeof(MetaType).GetMethod("__instancecheck__")
    Well, i realized it's not an instance method.
    OK, thank you so much.
     
  11. junestone

    junestone

    Joined:
    May 30, 2017
    Posts:
    42
    C
    Confirmed working.
    Thank you!!!

    PS: now I know why a guy said should use mono.cecil to inject IL code.
    Just inject code to any method like __instancecheck__, marking with MonoPInvokeAttribute, that will work too.
     
    JoshPeterson likes this.