2017-06-18 12:49:55 +00:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Reflection ;
using System.Reflection.Emit ;
using System.Runtime.InteropServices ;
using System.Text ;
2020-01-22 22:55:29 +00:00
using BizHawk.Common ;
namespace BizHawk.BizInvoke
2017-06-18 12:49:55 +00:00
{
public static class BizExvoker
{
/// <summary>
/// the assembly that all delegate types are placed in
/// </summary>
private static readonly AssemblyBuilder ImplAssemblyBuilder ;
/// <summary>
/// the module that all delegate types are placed in
/// </summary>
private static readonly ModuleBuilder ImplModuleBuilder ;
static BizExvoker ( )
{
var aname = new AssemblyName ( "BizExvokeProxyAssembly" ) ;
ImplAssemblyBuilder = AppDomain . CurrentDomain . DefineDynamicAssembly ( aname , AssemblyBuilderAccess . Run ) ;
ImplModuleBuilder = ImplAssemblyBuilder . DefineDynamicModule ( "BizExvokerModule" ) ;
}
/// <summary>
/// holds the delegate types for a type
/// </summary>
private class DelegateStorage
{
/// <summary>
/// the type that this storage was made for
/// </summary>
public Type OriginalType { get ; }
/// <summary>
/// the type that the delegate types reside in
/// </summary>
public Type StorageType { get ; }
public List < StoredDelegateInfo > DelegateTypes { get ; } = new List < StoredDelegateInfo > ( ) ;
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 < BizExportAttribute > ( ) . FirstOrDefault ( )
} )
2019-10-13 15:50:57 +00:00
. Where ( a = > a . Attr ! = null ) ;
2017-06-18 12:49:55 +00:00
2019-03-20 05:13:04 +00:00
var typeBuilder = ImplModuleBuilder . DefineType ( $"Bizhawk.BizExvokeHolder{type.Name}" , TypeAttributes . Class | TypeAttributes . Public | TypeAttributes . Sealed ) ;
2017-06-18 12:49:55 +00:00
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 < string , IntPtr > EntryPoints = new Dictionary < string , IntPtr > ( ) ;
private readonly List < Delegate > Delegates = new List < Delegate > ( ) ;
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 ) ) ;
}
}
2019-12-22 18:12:02 +00:00
public IntPtr ? GetProcAddrOrNull ( string entryPoint ) = > EntryPoints . TryGetValue ( entryPoint , out var ret ) ? ret : ( IntPtr ? ) null ;
2019-12-19 04:16:42 +00:00
public IntPtr GetProcAddrOrThrow ( string entryPoint ) = > GetProcAddrOrNull ( entryPoint ) ? ? throw new InvalidOperationException ( $"could not find {entryPoint} in exports" ) ;
2017-06-18 12:49:55 +00:00
}
static readonly Dictionary < Type , DelegateStorage > Impls = new Dictionary < Type , DelegateStorage > ( ) ;
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 ) ;
}
}
2020-02-18 15:10:24 +00:00
/// <summary>Indicates that a method is to be exported by BizExvoker.</summary>
2017-06-18 12:49:55 +00:00
[AttributeUsage(AttributeTargets.Method)]
2020-02-18 15:10:24 +00:00
public sealed class BizExportAttribute : Attribute
2017-06-18 12:49:55 +00:00
{
public CallingConvention CallingConvention { get ; }
2020-02-18 15:10:24 +00:00
/// <remarks>The annotated method's name is used iff <see langword="null"/>.</remarks>
2017-06-18 12:49:55 +00:00
public string EntryPoint { get ; set ; }
public BizExportAttribute ( CallingConvention c )
{
CallingConvention = c ;
}
}
}