You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Zero/ZeroLevel/Services/IL.cs

529 lines
25 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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<string, FieldBuilder> _fields = new Dictionary<string, FieldBuilder>();
private readonly Type _parentType;
private readonly Type[] _interfaces;
#endregion
/// <summary>
/// Конструктор
/// </summary>
/// <param name="typeName">Название создаваемого типа</param>
/// <param name="parentType">Тип от которого должен наследоваться создаваемый тип, если null, наследование от object</param>
/// <param name="interfaces">Интерфейсы которые должен реализовать создаваемый тип</param>
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;
}
/// <summary>
/// Собирает конечный тип
/// </summary>
/// <returns>Готовый тип</returns>
public Type Complete()
{
// Создание конструктора
CreateConstructor();
// Реализация методов интерфейсов
ProceedInterfaces();
// Реализация абстрактных методов родительского класса
ProceedParentAbstractMethods();
// Сборка типа
var type = _typeBuilder.CreateType();
// Результат
return type;
}
#region Private members
private readonly Dictionary<string, MethodBuilder> _methodsBuilderCachee = new Dictionary<string, MethodBuilder>();
/// <summary>
/// Создает конструктор принимающий метод обратного вызова
/// </summary>
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);
}
/// <summary>
/// Создает декортаторы для вызовов интерфейсных методов
/// </summary>
private void ProceedInterfaces()
{
var list = new List<Type>();
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<Type> GetInterfaces(Type[] sourceTypes)
{
var interfaces = new List<Type>();
IEnumerable<Type> subList = sourceTypes;
while (subList.Count() != 0)
{
subList = subList.SelectMany(i => i.GetInterfaces());
interfaces.AddRange(subList);
}
return interfaces;
}
/// <summary>
/// Создает декораторы для вызовов абстрактных методов родительского класса
/// </summary>
private void ProceedParentAbstractMethods()
{
if (_parentType != null)
{
foreach (var method in _parentType.GetMethods())
{
if (method.IsAbstract)
{
CreateProxyMethod(_typeBuilder, method, MethodType.AbstractOverrideProxy);
}
}
}
}
/// <summary>
/// Создание прокси-метода, вызывающего метод обратного вызова, передавая ему аргументы для реального метода
/// </summary>
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);
}
/// <summary>
/// Создает делегат вида public delegate object DecorateMethodCallHandler(string methodName, params object[] args);
/// </summary>
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<string, ModuleBuilder> _builders = new Dictionary<string, ModuleBuilder>();
private readonly static IDictionary<Type, Type> _types = new Dictionary<Type, Type>();
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
/// <summary>
/// Создание экземпляра прокси класса по интерфейсу <typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">Интерфейс</typeparam>
/// <param name="rpcHandler">Обработчик вызова метода</param>
/// <returns>Экземпляр прокси класса</returns>
internal static T CreateInterfaceInstance<T>(RemoteProcedureCallHandler rpcHandler)
{
var destType = GenerateInterfaceType<T>(rpcHandler);
return (T)Activator.CreateInstance(destType);
}
/// <summary>
/// Проверка корректности начальных условий
/// </summary>
/// <param name="sourceType">Тип интерфейса</param>
/// <param name="mi">Метод-обработчик удаленного вызова</param>
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");
}
/// <summary>
/// Получение динамического сбощика модуля
/// </summary>
/// <param name="sourceType">Тип интерфейса</param>
/// <returns>ModuleBuilder</returns>
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;
}
/// <summary>
/// Создание списка списка интерфейсов, по которому требуется создать прокси класс
/// В список входит указанный интерфейс <typeparamref name="T"/> и все интерфейсы от которых он унаследован
/// </summary>
/// <param name="sourceType">Тип интерфейса</param>
/// <returns>Список интерфейсов</returns>
internal static List<Type> GetDistinctInterfaces(Type sourceType)
{
var interfaces = new List<Type>();
IEnumerable<Type> subList = new[] { sourceType };
while (subList.Count() != 0)
{
interfaces.AddRange(subList);
subList = subList.SelectMany(i => i.GetInterfaces());
}
return interfaces.Distinct().ToList();
}
/// <summary>
/// Добавление нового метода в прокси тип
/// </summary>
/// <param name="typeBuilder">Сборщик типа</param>
/// <param name="method">Описание метода</param>
/// <param name="handler">Прокси метод</param>
/// <param name="contractName">Контракт к которому относится метод (Тип интерфейса)</param>
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);
}
/// <summary>
/// Создание прокси типа для интерфейса <typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">Интерфейс</typeparam>
/// <param name="rpcHandler">Прокси метод, для обработки вызова реального метода</param>
/// <returns>Созданный тип</returns>
private static Type GenerateInterfaceType<T>(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
}
}

Powered by TurnKey Linux.