From 426ae54ebb87871c30ab9e673597f4b4fc4b9bd4 Mon Sep 17 00:00:00 2001 From: Ogoun Date: Wed, 21 Sep 2022 20:34:20 +0300 Subject: [PATCH] Proxy emitter --- .../Services/FileSystem/BigFileParser.cs | 4 +- ZeroLevel/Services/IL.cs | 315 ++++++++++++++++++ ZeroLevel/ZeroLevel.csproj | 6 +- 3 files changed, 321 insertions(+), 4 deletions(-) create mode 100644 ZeroLevel/Services/IL.cs diff --git a/ZeroLevel/Services/FileSystem/BigFileParser.cs b/ZeroLevel/Services/FileSystem/BigFileParser.cs index ba73df7..4c451ba 100644 --- a/ZeroLevel/Services/FileSystem/BigFileParser.cs +++ b/ZeroLevel/Services/FileSystem/BigFileParser.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading; namespace ZeroLevel.Services.FileSystem { @@ -30,7 +31,7 @@ namespace ZeroLevel.Services.FileSystem } - public IEnumerable> ReadBatches(int batchSize) + public IEnumerable ReadBatches(int batchSize) { var buffer = new T[batchSize]; var buffer_index = 0; @@ -48,6 +49,7 @@ namespace ZeroLevel.Services.FileSystem if (buffer_index >= batchSize) { buffer_index = 0; + Thread.MemoryBarrier(); yield return buffer; } } diff --git a/ZeroLevel/Services/IL.cs b/ZeroLevel/Services/IL.cs new file mode 100644 index 0000000..72abce3 --- /dev/null +++ b/ZeroLevel/Services/IL.cs @@ -0,0 +1,315 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Reflection.PortableExecutable; + +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 + } +} diff --git a/ZeroLevel/ZeroLevel.csproj b/ZeroLevel/ZeroLevel.csproj index 9122ef0..dc078ba 100644 --- a/ZeroLevel/ZeroLevel.csproj +++ b/ZeroLevel/ZeroLevel.csproj @@ -6,7 +6,7 @@ ogoun ogoun - 3.3.6.4 + 3.3.6.5 Configuration floating number convert fix https://github.com/ogoun/Zero/wiki Copyright Ogoun 2022 @@ -14,8 +14,8 @@ https://github.com/ogoun/Zero git - 3.3.6.4 - 3.3.6.4 + 3.3.6.5 + 3.3.6.5 AnyCPU;x64;x86 zero.png full