public DelegateStorage(Type type) { var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public) .Select(m => new { Info = m, Attr = m.GetCustomAttributes(true).OfType <BizExportAttribute>().FirstOrDefault() }) .Where(a => a.Attr != null); 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; }
/// <summary> /// create a method implementation that uses GetDelegateForFunctionPointer internally /// </summary> private static Action <object, IImportResolver, ICallingConventionAdapter> ImplementMethodDelegate( TypeBuilder type, MethodInfo baseMethod, CallingConvention nativeCall, string entryPointName, FieldInfo?monitorField, bool nonTrivialAdapter) { // create the delegate type var delegateType = BizInvokeUtilities.CreateDelegateType(baseMethod, nativeCall, type, out var delegateInvoke); var paramInfos = baseMethod.GetParameters(); var paramTypes = paramInfos.Select(p => p.ParameterType).ToArray(); var returnType = baseMethod.ReturnType; if (paramTypes.Concat(new[] { returnType }).Any(typeof(Delegate).IsAssignableFrom)) { // this isn't a problem if CallingConventionAdapters.Waterbox is a no-op, but it is otherwise: we don't // have a custom marshaller set up so the user needs to manually pump the callingconventionadapter if (nonTrivialAdapter) { throw new InvalidOperationException( "Compatibility call mode cannot use ICallingConventionAdapters for automatically marshalled delegate types!"); } } // define a field on the class to hold the delegate var field = type.DefineField( $"DelegateField{baseMethod.Name}", delegateType, FieldAttributes.Public); var method = type.DefineMethod( baseMethod.Name, MethodAttributes.Virtual | MethodAttributes.Public, CallingConventions.HasThis, returnType, paramTypes); var il = method.GetILGenerator(); Label exc = new Label(); if (monitorField != null) // monitor: enter and then begin try { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, monitorField); il.Emit(OpCodes.Callvirt, MInfo_IMonitor_Enter); exc = il.BeginExceptionBlock(); } il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, field); for (int i = 0; i < paramTypes.Length; i++) { il.Emit(OpCodes.Ldarg, (short)(i + 1)); } il.Emit(OpCodes.Callvirt, delegateInvoke); if (monitorField != null) // monitor: finally exit { LocalBuilder?loc = null; if (returnType != typeof(void)) { loc = il.DeclareLocal(returnType); il.Emit(OpCodes.Stloc, loc); } il.Emit(OpCodes.Leave, exc); il.BeginFinallyBlock(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, monitorField); il.Emit(OpCodes.Callvirt, MInfo_IMonitor_Exit); il.EndExceptionBlock(); if (returnType != typeof(void)) { il.Emit(OpCodes.Ldloc, loc !); } } il.Emit(OpCodes.Ret); type.DefineMethodOverride(method, baseMethod); return((o, dll, adapter) => { var entryPtr = dll.GetProcAddrOrThrow(entryPointName); var interopDelegate = adapter.GetDelegateForFunctionPointer(entryPtr, delegateType.CreateType()); o.GetType().GetField(field.Name).SetValue(o, interopDelegate); }); }