using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Text;
namespace BizHawk.Common.BizInvoke
{
public static class BizExvoker
{
///
/// the assembly that all delegate types are placed in
///
private static readonly AssemblyBuilder ImplAssemblyBuilder;
///
/// the module that all delegate types are placed in
///
private static readonly ModuleBuilder ImplModuleBuilder;
static BizExvoker()
{
var aname = new AssemblyName("BizExvokeProxyAssembly");
ImplAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.Run);
ImplModuleBuilder = ImplAssemblyBuilder.DefineDynamicModule("BizExvokerModule");
}
///
/// holds the delegate types for a type
///
private class DelegateStorage
{
///
/// the type that this storage was made for
///
public Type OriginalType { get; }
///
/// the type that the delegate types reside in
///
public Type StorageType { get; }
public List DelegateTypes { get; } = new List();
public class StoredDelegateInfo
{
public MethodInfo Method { get; }
public Type DelegateType { get; }
public string EntryPointName { get; }
public StoredDelegateInfo(MethodInfo method, Type delegateType, string entryPointName)
{
Method = method;
DelegateType = delegateType;
EntryPointName = entryPointName;
}
}
public DelegateStorage(Type type)
{
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public)
.Select(m => new
{
Info = m,
Attr = m.GetCustomAttributes(true).OfType().FirstOrDefault()
})
.Where(a => a.Attr != null)
.ToList();
var typeBuilder = ImplModuleBuilder.DefineType(
"Bizhawk.BizExvokeHolder" + type.Name, TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed);
foreach (var a in methods)
{
MethodBuilder unused;
var delegateType = BizInvokeUtilities.CreateDelegateType(a.Info, a.Attr.CallingConvention, typeBuilder, out unused).CreateType();
DelegateTypes.Add(new StoredDelegateInfo(a.Info, delegateType, a.Attr.EntryPoint ?? a.Info.Name));
}
StorageType = typeBuilder.CreateType();
OriginalType = type;
}
}
private class ExvokerImpl : IImportResolver
{
private readonly Dictionary EntryPoints = new Dictionary();
private readonly List Delegates = new List();
public ExvokerImpl(object o, DelegateStorage d, ICallingConventionAdapter a)
{
foreach (var sdt in d.DelegateTypes)
{
var del = Delegate.CreateDelegate(sdt.DelegateType, o, sdt.Method);
Delegates.Add(del); // prevent garbage collection of the delegate, which would invalidate the pointer
EntryPoints.Add(sdt.EntryPointName, a.GetFunctionPointerForDelegate(del));
}
}
public IntPtr Resolve(string entryPoint)
{
IntPtr ret;
EntryPoints.TryGetValue(entryPoint, out ret);
return ret;
}
}
static readonly Dictionary Impls = new Dictionary();
public static IImportResolver GetExvoker(object o, ICallingConventionAdapter a)
{
DelegateStorage ds;
lock (Impls)
{
var type = o.GetType();
if (!Impls.TryGetValue(type, out ds))
{
ds = new DelegateStorage(type);
Impls.Add(type, ds);
}
}
return new ExvokerImpl(o, ds, a);
}
}
///
/// mark an instance method to be exported by BizExvoker
///
[AttributeUsage(AttributeTargets.Method)]
public class BizExportAttribute : Attribute
{
public CallingConvention CallingConvention { get; }
///
/// Gets or sets the name of entry point; if not given, the method's name is used
///
public string EntryPoint { get; set; }
///
/// Initializes a new instance of the class.
///
/// unmanaged calling convention
public BizExportAttribute(CallingConvention c)
{
CallingConvention = c;
}
}
}