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