using System; using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace SharpGen.Runtime { [DebuggerTypeProxy(typeof(CppObjectVtblDebugView))] public class CppObjectVtbl { // We need to store the original delegate instances, because Marshal.GetFunctionPointerForDelegate // doesn't create GC roots for the source delegates. private readonly Delegate[] delegates; private uint vtblMaxIndex; /// /// Default Constructor. /// /// number of methods to allocate in the VTBL public CppObjectVtbl(int numberOfCallbackMethods) { // Allocate ptr to vtbl Pointer = Marshal.AllocHGlobal(IntPtr.Size * numberOfCallbackMethods); delegates = new Delegate[numberOfCallbackMethods]; } /// /// Gets the pointer to the vtbl. /// public IntPtr Pointer { get; } /// /// Add a method supported by this interface. This method is typically called from inherited constructor. /// /// the managed delegate method /// the index in the vtable for this method. [MethodImpl(MethodImplOptions.AggressiveInlining)] protected void AddMethod(Delegate method, int index) => AddMethod(method, (uint) index); /// /// Add a method supported by this interface. This method is typically called from inherited constructor. /// /// the managed delegate method /// the index in the vtable for this method. protected unsafe void AddMethod(Delegate method, uint index) { if (index > vtblMaxIndex) vtblMaxIndex = index; delegates[index] = method; *((IntPtr*) Pointer + index) = Marshal.GetFunctionPointerForDelegate(method); } /// /// Add a method supported by this interface. This method is typically called from inherited constructor. /// /// the unmanaged function pointer /// the index in the vtable for this method. protected unsafe void AddMethod(void* method, uint index) { if (index > vtblMaxIndex) vtblMaxIndex = index; delegates[index] = null; *((void**) Pointer + index) = method; } /// /// Add a method supported by this interface at the current end of the vtable. This method is typically called from inherited constructor. /// /// the managed delegate method [Obsolete("Use AddMethod(Delegate,int) to explicitly specify the index of the delegate in the vtable.")] [EditorBrowsable(EditorBrowsableState.Advanced)] protected void AddMethod(Delegate method) => AddMethod(method, vtblMaxIndex + 1); protected static T ToShadow(IntPtr thisPtr) where T : CppObjectShadow => CppObjectShadow.ToShadow(thisPtr); public override string ToString() => $"0x{Pointer.ToInt64():X} @ Count={delegates.Length}"; protected sealed class CppObjectVtblDebugView { private readonly CppObjectVtbl vtbl; public CppObjectVtblDebugView(CppObjectVtbl vtbl) => this.vtbl = vtbl; [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] public unsafe object[] Items { get { var delegates = vtbl.delegates; var vtblPointer = vtbl.Pointer; var length = delegates.Length; var items = new object[length]; for (uint i = 0; i < length; i++) items[i] = delegates[i] ?? (object) new FunctionPointerItem(*((IntPtr*) vtblPointer + i)); return items; } } private readonly struct FunctionPointerItem { private readonly IntPtr pointer; public FunctionPointerItem(IntPtr pointer) => this.pointer = pointer; public override string ToString() => $"0x{pointer.ToInt64():X}"; } } } }