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}";
}
}
}
}