using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; namespace ZeroLevel.Services { public delegate object DecorateMethodCallHandler(string methodName, params object[] args); public class ProxyTypeBuilder { private enum MethodType { DirectProxy, AbstractOverrideProxy } #region Fields private readonly AssemblyBuilder _assemblyBuilder; private readonly ModuleBuilder _moduleBuilder; private readonly TypeBuilder _typeBuilder; private readonly FieldBuilder _callbackField; private readonly IDictionary _fields = new Dictionary(); private readonly Type _parentType; private readonly Type[] _interfaces; #endregion /// /// Конструктор /// /// Название создаваемого типа /// Тип от которого должен наследоваться создаваемый тип, если null, наследование от object /// Интерфейсы которые должен реализовать создаваемый тип public ProxyTypeBuilder(string typeName, Type parentType, params Type[] interfaces) { var newAssemblyName = new AssemblyName(Guid.NewGuid().ToString()); _assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(newAssemblyName, AssemblyBuilderAccess.Run); _moduleBuilder = _assemblyBuilder.DefineDynamicModule(newAssemblyName.Name); _typeBuilder = _moduleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.Serializable | TypeAttributes.AutoLayout, parentType ?? typeof(object), interfaces); // Поле для хранения метода обратного вызова _callbackField = _typeBuilder.DefineField("_callbackHandler", CreateDecorateMethodCallHandlerDelegate(_moduleBuilder), FieldAttributes.Private); _interfaces = interfaces; _parentType = parentType!; } /// /// Собирает конечный тип /// /// Готовый тип public Type Complete() { // Создание конструктора CreateConstructor(); // Реализация методов интерфейсов ProceedInterfaces(); // Реализация абстрактных методов родительского класса ProceedParentAbstractMethods(); // Сборка типа var type = _typeBuilder.CreateType(); // Результат return type; } #region Private members private readonly Dictionary _methodsBuilderCachee = new Dictionary(); /// /// Создает конструктор принимающий метод обратного вызова /// private void CreateConstructor() { Type objType = Type.GetType("System.Object"); ConstructorInfo objCtor = objType.GetConstructor(new Type[0]); ConstructorBuilder cBuilder = _typeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[1] { typeof(DecorateMethodCallHandler) }); ILGenerator cil = cBuilder.GetILGenerator(); cil.Emit(OpCodes.Ldarg_0); cil.Emit(OpCodes.Call, objCtor); cil.Emit(OpCodes.Nop); cil.Emit(OpCodes.Nop); // Присвоение полю класса _callbackHandler аргумента конструктора cil.Emit(OpCodes.Ldarg_0); cil.Emit(OpCodes.Ldarg_1); cil.Emit(OpCodes.Stfld, _callbackField); cil.Emit(OpCodes.Nop); cil.Emit(OpCodes.Ret); } /// /// Создает декортаторы для вызовов интерфейсных методов /// private void ProceedInterfaces() { var list = new List(); if (_interfaces != null && _interfaces.Length > 0) { list.AddRange(_interfaces); list.AddRange(GetInterfaces(_interfaces)); } if (_parentType != null!) { list.AddRange(GetInterfaces(new Type[] { _parentType })); } list = list.Distinct().ToList(); foreach (var interfaceType in list) { var properties = interfaceType.GetProperties(); foreach (var property in properties) { _fields.Add(property.Name, _typeBuilder.DefineField("_" + property.Name, property.PropertyType, FieldAttributes.Private)); } foreach (var method in interfaceType.GetMethods()) { if (properties.Any(p => p.GetGetMethod() == method || p.GetSetMethod() == method)) { continue; //CreateProxyMethod(_typeBuilder, method, MethodType.DirectProxy, true); } else { CreateProxyMethod(_typeBuilder, method, MethodType.DirectProxy); } } foreach (var propertyInfo in properties) { AddProperty(_typeBuilder, propertyInfo.Name, propertyInfo.PropertyType); } } } private static IEnumerable GetInterfaces(Type[] sourceTypes) { var interfaces = new List(); IEnumerable subList = sourceTypes; while (subList.Count() != 0) { subList = subList.SelectMany(i => i.GetInterfaces()); interfaces.AddRange(subList); } return interfaces; } /// /// Создает декораторы для вызовов абстрактных методов родительского класса /// private void ProceedParentAbstractMethods() { if (_parentType != null!) { foreach (var method in _parentType.GetMethods()) { if (method.IsAbstract) { CreateProxyMethod(_typeBuilder, method, MethodType.AbstractOverrideProxy); } } } } /// /// Создание прокси-метода, вызывающего метод обратного вызова, передавая ему аргументы для реального метода /// private void CreateProxyMethod(TypeBuilder typeBuilder, MethodInfo method, MethodType methodType, bool isPropertyAccessor = false) { var methodName = method.Name; var returnType = method.ReturnType; var parametersTypes = method.GetParameters().Select(p => p.ParameterType).ToArray(); var hasReturnValue = returnType != typeof(void); MethodBuilder dynamicMethod = null!; switch (methodType) { case MethodType.DirectProxy: dynamicMethod = typeBuilder.DefineMethod(methodName, method.Attributes ^ MethodAttributes.Abstract, method.CallingConvention, returnType, method.ReturnParameter.GetRequiredCustomModifiers(), method.ReturnParameter.GetOptionalCustomModifiers(), parametersTypes, method.GetParameters().Select(p => p.GetRequiredCustomModifiers()).ToArray(), method.GetParameters().Select(p => p.GetOptionalCustomModifiers()).ToArray()); break; case MethodType.AbstractOverrideProxy: dynamicMethod = typeBuilder.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.Final, method.CallingConvention, returnType, method.ReturnParameter.GetRequiredCustomModifiers(), method.ReturnParameter.GetOptionalCustomModifiers(), parametersTypes, method.GetParameters().Select(p => p.GetRequiredCustomModifiers()).ToArray(), method.GetParameters().Select(p => p.GetOptionalCustomModifiers()).ToArray()); break; } int index = 1; foreach (var p in method.GetParameters()) { dynamicMethod.DefineParameter(index, ParameterAttributes.In, p.Name); index++; } var il = dynamicMethod.GetILGenerator(); il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, _callbackField); ParameterInfo[] parameters = method.GetParameters(); // Массив для параметров оригинального метода LocalBuilder argArray = il.DeclareLocal(typeof(object[])); il.Emit(OpCodes.Ldc_I4, parameters.Length); il.Emit(OpCodes.Newarr, typeof(object)); il.Emit(OpCodes.Stloc, argArray); // Заполнение массива for (int i = 0; i < parameters.Length; i++) { ParameterInfo info = parameters[i]; il.Emit(OpCodes.Ldloc, argArray); il.Emit(OpCodes.Ldc_I4, i); il.Emit(OpCodes.Ldarg_S, i + 1); if (info.ParameterType.IsPrimitive || info.ParameterType.IsValueType) il.Emit(OpCodes.Box, info.ParameterType); il.Emit(OpCodes.Stelem_Ref); } // Аргументы прокси-метода il.Emit(OpCodes.Ldstr, method.Name); il.Emit(OpCodes.Ldloc, argArray); // Вызов прокси-метода il.Emit(OpCodes.Callvirt, typeof(DecorateMethodCallHandler).GetMethod("Invoke")); // Возврат результата if (hasReturnValue) { if (returnType.IsValueType) { il.Emit(OpCodes.Unbox_Any, returnType); } } else { il.Emit(OpCodes.Pop); } il.Emit(OpCodes.Ret); } /// /// Создает делегат вида public delegate object DecorateMethodCallHandler(string methodName, params object[] args); /// private static Type CreateDecorateMethodCallHandlerDelegate(ModuleBuilder moduleBuilder) { TypeBuilder typeBuilder = moduleBuilder.DefineType("DecorateMethodCallHandler", TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.AnsiClass | TypeAttributes.AutoClass, typeof(MulticastDelegate)); var constructorBuilder = typeBuilder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(object), typeof(System.IntPtr) }); constructorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); var methodBuilder = typeBuilder.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, typeof(object), new[] { typeof(string), typeof(object[]) }); methodBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); return typeBuilder.CreateType(); } private static void AddProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType) { var fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private); var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null); var getMethod = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes); var getMethodIL = getMethod.GetILGenerator(); getMethodIL.Emit(OpCodes.Ldarg_0); getMethodIL.Emit(OpCodes.Ldfld, fieldBuilder); getMethodIL.Emit(OpCodes.Ret); var setMethod = typeBuilder.DefineMethod("set_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new[] { propertyType }); var setMethodIL = setMethod.GetILGenerator(); Label modifyProperty = setMethodIL.DefineLabel(); Label exitSet = setMethodIL.DefineLabel(); setMethodIL.MarkLabel(modifyProperty); setMethodIL.Emit(OpCodes.Ldarg_0); setMethodIL.Emit(OpCodes.Ldarg_1); setMethodIL.Emit(OpCodes.Stfld, fieldBuilder); setMethodIL.Emit(OpCodes.Nop); setMethodIL.MarkLabel(exitSet); setMethodIL.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(getMethod); propertyBuilder.SetSetMethod(setMethod); } #endregion } public delegate object RemoteProcedureCallHandler(string contractName, string methodName, string returnTypeFullName, params object[] args); internal static class DynamicProxyGenerator { #region Private Fields private readonly static IDictionary _builders = new Dictionary(); private readonly static IDictionary _types = new Dictionary(); private readonly static object _lockObject = new object(); #endregion /* #region Public Methods // Note that calling this method will cause any further // attempts to generate an interface to fail internal static void Save() { foreach (var builder in _builders.Select(b => b.Value)) { var ass = (AssemblyBuilder)builder.Assembly; try { ass.Save(ass.GetName().Name + ".dll"); } catch { } } } #endregion */ #region Private Methods /// /// Создание экземпляра прокси класса по интерфейсу /// /// Интерфейс /// Обработчик вызова метода /// Экземпляр прокси класса internal static T CreateInterfaceInstance(RemoteProcedureCallHandler rpcHandler) { var destType = GenerateInterfaceType(rpcHandler); return (T)Activator.CreateInstance(destType); } /// /// Проверка корректности начальных условий /// /// Тип интерфейса /// Метод-обработчик удаленного вызова private static void Validate(Type sourceType, MethodInfo mi) { if (!sourceType.IsInterface) throw new ArgumentException("Type T is not an interface", "T"); if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public) throw new ArgumentException("Method must be public.", "getter"); } /// /// Получение динамического сбощика модуля /// /// Тип интерфейса /// ModuleBuilder private static ModuleBuilder CreateModuleBuilder(Type sourceType) { var orginalAssemblyName = sourceType.Assembly.GetName().Name; ModuleBuilder moduleBuilder; if (!_builders.TryGetValue(orginalAssemblyName, out moduleBuilder)) { var newAssemblyName = new AssemblyName(Guid.NewGuid() + "." + orginalAssemblyName); var dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(newAssemblyName, AssemblyBuilderAccess.RunAndCollect); moduleBuilder = dynamicAssembly.DefineDynamicModule(newAssemblyName.Name); _builders.Add(orginalAssemblyName, moduleBuilder); } return moduleBuilder; } /// /// Создание списка списка интерфейсов, по которому требуется создать прокси класс /// В список входит указанный интерфейс и все интерфейсы от которых он унаследован /// /// Тип интерфейса /// Список интерфейсов internal static List GetDistinctInterfaces(Type sourceType) { var interfaces = new List(); IEnumerable subList = new[] { sourceType }; while (subList.Count() != 0) { interfaces.AddRange(subList); subList = subList.SelectMany(i => i.GetInterfaces()); } return interfaces.Distinct().ToList(); } /// /// Добавление нового метода в прокси тип /// /// Сборщик типа /// Описание метода /// Прокси метод /// Контракт к которому относится метод (Тип интерфейса) private static void AppendMethodToProxy(TypeBuilder typeBuilder, MethodInfo method, RemoteProcedureCallHandler handler, string contractName) { string methodName = method.Name; Type retType = method.ReturnType; bool hasReturnValue = retType != typeof(void); var newMethod = typeBuilder.DefineMethod(methodName, method.Attributes ^ MethodAttributes.Abstract, method.CallingConvention, retType, method.ReturnParameter.GetRequiredCustomModifiers(), method.ReturnParameter.GetOptionalCustomModifiers(), method.GetParameters().Select(p => p.ParameterType).ToArray(), method.GetParameters().Select(p => p.GetRequiredCustomModifiers()).ToArray(), method.GetParameters().Select(p => p.GetOptionalCustomModifiers()).ToArray() ); var il = newMethod.GetILGenerator(); /* Type exType = typeof(Exception); ConstructorInfo exCtorInfo = exType.GetConstructor(new Type[] { typeof(string) }); MethodInfo exToStrMI = exType.GetMethod("ToString"); MethodInfo writeLineMI = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string), typeof(object) }); LocalBuilder tmp2 = il.DeclareLocal(exType);*/ ParameterInfo[] parameters = method.GetParameters(); // Массив для параметров оригинального метода LocalBuilder argArray = il.DeclareLocal(typeof(object[])); il.Emit(OpCodes.Ldc_I4, parameters.Length); il.Emit(OpCodes.Newarr, typeof(object)); il.Emit(OpCodes.Stloc, argArray); // Заполнение массива for (int i = 0; i < parameters.Length; i++) { ParameterInfo info = parameters[i]; il.Emit(OpCodes.Ldloc, argArray); il.Emit(OpCodes.Ldc_I4, i); il.Emit(OpCodes.Ldarg_S, i + 1); if (info.ParameterType.IsPrimitive || info.ParameterType.IsValueType) il.Emit(OpCodes.Box, info.ParameterType); il.Emit(OpCodes.Stelem_Ref); } // Аргументы прокси-метода il.Emit(OpCodes.Ldstr, contractName); il.Emit(OpCodes.Ldstr, methodName); if (hasReturnValue) { il.Emit(OpCodes.Ldstr, retType.FullName); } else { il.Emit(OpCodes.Ldstr, typeof(void).FullName); } il.Emit(OpCodes.Ldloc, argArray); // Вызов прокси-метода // Label exBlock = il.BeginExceptionBlock(); il.EmitCall(OpCodes.Call, handler.GetMethodInfo(), null); /* il.Emit(OpCodes.Stloc_S, tmp2); il.Emit(OpCodes.Ldstr, "Caught {0}"); il.Emit(OpCodes.Ldloc_S, tmp2); il.EmitCall(OpCodes.Callvirt, exToStrMI, null); il.EmitCall(OpCodes.Call, writeLineMI, null); il.Emit(OpCodes.Ldc_I4_M1); il.EndExceptionBlock(); */ // Возврат результата if (hasReturnValue) { if (retType.IsValueType) il.Emit(OpCodes.Unbox_Any, retType); } else { il.Emit(OpCodes.Pop); } il.Emit(OpCodes.Ret); } /// /// Создание прокси типа для интерфейса /// /// Интерфейс /// Прокси метод, для обработки вызова реального метода /// Созданный тип private static Type GenerateInterfaceType(RemoteProcedureCallHandler rpcHandler) { var sourceType = typeof(T); Type newType; if (_types.TryGetValue(sourceType, out newType)) return newType; string sourceContractFullName = sourceType.FullName; // Make sure the same interface isn't implemented twice lock (_lockObject) { if (_types.TryGetValue(sourceType, out newType)) return newType; // Validation Validate(sourceType, rpcHandler.Method); // Module and Assembly Creation var moduleBuilder = CreateModuleBuilder(sourceType); var assemblyName = moduleBuilder.Assembly.GetName(); // Create the TypeBuilder var typeBuilder = moduleBuilder.DefineType(sourceType.FullName, TypeAttributes.Public | TypeAttributes.Class, typeof(object), new[] { sourceType }); // Enumerate interface inheritance hierarchy var interfaces = GetDistinctInterfaces(sourceType); // Create the methods foreach (var method in interfaces.SelectMany(i => i.GetMethods())) { AppendMethodToProxy(typeBuilder, method, rpcHandler, sourceContractFullName); } // Create and return the defined type newType = typeBuilder.CreateType(); _types.Add(sourceType, newType); return newType; } } #endregion } }