IL 生成用户定义函数的代理<TInput, TReturn>
IL Generate Proxy to User Define Func<TInput, TReturn>
我正在尝试动态生成程序集来调用用户创建的函数。
private MethodBuilder DefineGetMethod<TInput, TReturn>(
TypeBuilder tb, MethodDescriptor methodInfo, Func<TInput, TReturn> dynamicMethod)
{
//Define the function
var dynamicMethodBuilder = tb.DefineMethod(methodInfo.MethodName,
MethodAttributes.Public,
methodInfo.ReturnType, methodInfo.InputParameters.Select(x => x.Type).ToArray());
//Define the labels for the method inputs
for(var i = 0; i < methodInfo.InputParameters.Length; i++ )
{
// Position 0 is the return value, 1 is the 1st param, 2 is 2nd, etc.
var position = 1 + i;
var inputParam = methodInfo.InputParameters[i];
dynamicMethodBuilder.DefineParameter(position, ParameterAttributes.None, inputParam.Name);
}
var ilGenerator = dynamicMethodBuilder.GetILGenerator();
//Loads arg1
ilGenerator.Emit(OpCodes.Ldarg_1);
//Not sure how to pass the arg1 to the method body to return
var ilMethodBody = dynamicMethod.Method.GetMethodBody().GetILAsByteArray();
//Generates return
ilGenerator.Emit(OpCodes.Ret);
}
编辑
我反编译了现有代码,以与反编译现有代码类似的方式调用方法,但我仍然无法让它工作
// Argument 1 of dynamic method is argument array.
myMethodIL.Emit(OpCodes.Ldarg_1);
myMethodIL.Emit(OpCodes.Callvirt, method.Method);
myMethodIL.Emit(OpCodes.Stloc_0);
myMethodIL.Emit(OpCodes.Ldloc_0);
myMethodIL.Emit(OpCodes.Ret);
如何将加载的参数传递给 ilMethodBody
和 return?
编辑 - 约翰尼 5
原来只需要跳转到已有的方法信息即可:
myMethodIL.Emit(OpCodes.Jmp, method.Method);
myMethodIL.Emit(OpCodes.Ret);
首先,您必须确保 TReturn
等于 methodInfo.ReturnType
并且 TInput
等于 methodInfo.InputParameters
中的第一个。
如果dynamicMethod
是一个静态方法委托,它会很容易像:
private MethodBuilder DefineGetMethod<TInput, TReturn>(TypeBuilder tb, MethodDescriptor methodInfo, Func<TInput, TReturn> dynamicMethod)
{
var dynamicMethodBuilder = tb.DefineMethod(methodInfo.MethodName,
MethodAttributes.Public | MethodAttributes.HideBySig,
methodInfo.ReturnType, methodInfo.InputParameters.Select(x => x.Type).ToArray());
var ilGenerator = dynamicMethodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit(OpCodes.Call, dynamicMethod.Method);
ilGenerator.Emit(OpCodes.Ret);
return dynamicMethodBuilder;
}
但是如果 dynamicMethod
可能是一个实例方法(lambda 是一个实例方法),那就很难了。调用实例方法需要先将实例压入栈中,但Emit
只允许压入常量值,如int、string。
我只能想到一个方法,声明一个字段存储dynamicMethod
,通过reflect在构建类型后设置字段值:
private Type DefineGetMethod<TInput, TReturn>(TypeBuilder tb, MethodDescriptor methodInfo, Func<TInput, TReturn> dynamicMethod)
{
var fieldBuilder = tb.DefineField("_func", dynamicMethod.GetType(), FieldAttributes.Private | FieldAttributes.Static);
var dynamicMethodBuilder = tb.DefineMethod(methodInfo.MethodName,
MethodAttributes.Public | MethodAttributes.HideBySig,
methodInfo.ReturnType, methodInfo.InputParameters.Select(x => x.Type).ToArray());
var ilGenerator = dynamicMethodBuilder.GetILGenerator();
// load static field _func onto stack
ilGenerator.Emit(OpCodes.Ldsfld, fieldBuilder);
// load arg1 onto stack
ilGenerator.Emit(OpCodes.Ldarg_1);
// call _func.Invoke(..)
ilGenerator.Emit(OpCodes.Callvirt, dynamicMethod.GetType().GetMethod("Invoke"));
ilGenerator.Emit(OpCodes.Ret);
var type = tb.CreateType();
var field = type.GetField("_func", BindingFlags.NonPublic | BindingFlags.Static);
// store dynamicMethod into static field _func
field.SetValue(null, dynamicMethod);
return type;
}
编辑
测试代码:
class Program
{
static void Main(string[] args)
{
OnlyStaticFunc();
StaticField();
}
static void OnlyStaticFunc()
{
Func<string, int> func = int.Parse;
var assemblyName = new AssemblyName("StaticFuncTest");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
var typeBuilder = moduleBuilder.DefineType("Abc", TypeAttributes.Public);
var methodBuilder = typeBuilder.DefineMethod("Execute", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(int), new[] { typeof(string) });
var il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, func.Method);
il.Emit(OpCodes.Ret);
var type = typeBuilder.CreateType();
var abc = Activator.CreateInstance(type);
var value = ((dynamic)abc).Execute("123");
Console.WriteLine($"only static func: {value}");
}
static void StaticField()
{
Func<string, int> func = s => int.Parse(s);
var assemblyName = new AssemblyName("StaticFieldTest");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
var typeBuilder = moduleBuilder.DefineType("Abc", TypeAttributes.Public);
var fieldBuilder = typeBuilder.DefineField("_func", func.GetType(), FieldAttributes.Private | FieldAttributes.Static);
var methodBuilder = typeBuilder.DefineMethod("Execute", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(int), new[] { typeof(string) });
var il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldsfld, fieldBuilder);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Callvirt, func.GetType().GetMethod("Invoke"));
il.Emit(OpCodes.Ret);
var type = typeBuilder.CreateType();
var field = type.GetField("_func", BindingFlags.NonPublic | BindingFlags.Static);
field.SetValue(null, func);
var abc = Activator.CreateInstance(type);
var value = ((dynamic)abc).Execute("456");
Console.WriteLine($"static field: {value}");
}
}
我正在尝试动态生成程序集来调用用户创建的函数。
private MethodBuilder DefineGetMethod<TInput, TReturn>(
TypeBuilder tb, MethodDescriptor methodInfo, Func<TInput, TReturn> dynamicMethod)
{
//Define the function
var dynamicMethodBuilder = tb.DefineMethod(methodInfo.MethodName,
MethodAttributes.Public,
methodInfo.ReturnType, methodInfo.InputParameters.Select(x => x.Type).ToArray());
//Define the labels for the method inputs
for(var i = 0; i < methodInfo.InputParameters.Length; i++ )
{
// Position 0 is the return value, 1 is the 1st param, 2 is 2nd, etc.
var position = 1 + i;
var inputParam = methodInfo.InputParameters[i];
dynamicMethodBuilder.DefineParameter(position, ParameterAttributes.None, inputParam.Name);
}
var ilGenerator = dynamicMethodBuilder.GetILGenerator();
//Loads arg1
ilGenerator.Emit(OpCodes.Ldarg_1);
//Not sure how to pass the arg1 to the method body to return
var ilMethodBody = dynamicMethod.Method.GetMethodBody().GetILAsByteArray();
//Generates return
ilGenerator.Emit(OpCodes.Ret);
}
编辑
我反编译了现有代码,以与反编译现有代码类似的方式调用方法,但我仍然无法让它工作
// Argument 1 of dynamic method is argument array.
myMethodIL.Emit(OpCodes.Ldarg_1);
myMethodIL.Emit(OpCodes.Callvirt, method.Method);
myMethodIL.Emit(OpCodes.Stloc_0);
myMethodIL.Emit(OpCodes.Ldloc_0);
myMethodIL.Emit(OpCodes.Ret);
如何将加载的参数传递给 ilMethodBody
和 return?
编辑 - 约翰尼 5
原来只需要跳转到已有的方法信息即可:
myMethodIL.Emit(OpCodes.Jmp, method.Method);
myMethodIL.Emit(OpCodes.Ret);
首先,您必须确保 TReturn
等于 methodInfo.ReturnType
并且 TInput
等于 methodInfo.InputParameters
中的第一个。
如果dynamicMethod
是一个静态方法委托,它会很容易像:
private MethodBuilder DefineGetMethod<TInput, TReturn>(TypeBuilder tb, MethodDescriptor methodInfo, Func<TInput, TReturn> dynamicMethod)
{
var dynamicMethodBuilder = tb.DefineMethod(methodInfo.MethodName,
MethodAttributes.Public | MethodAttributes.HideBySig,
methodInfo.ReturnType, methodInfo.InputParameters.Select(x => x.Type).ToArray());
var ilGenerator = dynamicMethodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit(OpCodes.Call, dynamicMethod.Method);
ilGenerator.Emit(OpCodes.Ret);
return dynamicMethodBuilder;
}
但是如果 dynamicMethod
可能是一个实例方法(lambda 是一个实例方法),那就很难了。调用实例方法需要先将实例压入栈中,但Emit
只允许压入常量值,如int、string。
我只能想到一个方法,声明一个字段存储dynamicMethod
,通过reflect在构建类型后设置字段值:
private Type DefineGetMethod<TInput, TReturn>(TypeBuilder tb, MethodDescriptor methodInfo, Func<TInput, TReturn> dynamicMethod)
{
var fieldBuilder = tb.DefineField("_func", dynamicMethod.GetType(), FieldAttributes.Private | FieldAttributes.Static);
var dynamicMethodBuilder = tb.DefineMethod(methodInfo.MethodName,
MethodAttributes.Public | MethodAttributes.HideBySig,
methodInfo.ReturnType, methodInfo.InputParameters.Select(x => x.Type).ToArray());
var ilGenerator = dynamicMethodBuilder.GetILGenerator();
// load static field _func onto stack
ilGenerator.Emit(OpCodes.Ldsfld, fieldBuilder);
// load arg1 onto stack
ilGenerator.Emit(OpCodes.Ldarg_1);
// call _func.Invoke(..)
ilGenerator.Emit(OpCodes.Callvirt, dynamicMethod.GetType().GetMethod("Invoke"));
ilGenerator.Emit(OpCodes.Ret);
var type = tb.CreateType();
var field = type.GetField("_func", BindingFlags.NonPublic | BindingFlags.Static);
// store dynamicMethod into static field _func
field.SetValue(null, dynamicMethod);
return type;
}
编辑
测试代码:
class Program
{
static void Main(string[] args)
{
OnlyStaticFunc();
StaticField();
}
static void OnlyStaticFunc()
{
Func<string, int> func = int.Parse;
var assemblyName = new AssemblyName("StaticFuncTest");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
var typeBuilder = moduleBuilder.DefineType("Abc", TypeAttributes.Public);
var methodBuilder = typeBuilder.DefineMethod("Execute", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(int), new[] { typeof(string) });
var il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, func.Method);
il.Emit(OpCodes.Ret);
var type = typeBuilder.CreateType();
var abc = Activator.CreateInstance(type);
var value = ((dynamic)abc).Execute("123");
Console.WriteLine($"only static func: {value}");
}
static void StaticField()
{
Func<string, int> func = s => int.Parse(s);
var assemblyName = new AssemblyName("StaticFieldTest");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
var typeBuilder = moduleBuilder.DefineType("Abc", TypeAttributes.Public);
var fieldBuilder = typeBuilder.DefineField("_func", func.GetType(), FieldAttributes.Private | FieldAttributes.Static);
var methodBuilder = typeBuilder.DefineMethod("Execute", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(int), new[] { typeof(string) });
var il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldsfld, fieldBuilder);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Callvirt, func.GetType().GetMethod("Invoke"));
il.Emit(OpCodes.Ret);
var type = typeBuilder.CreateType();
var field = type.GetField("_func", BindingFlags.NonPublic | BindingFlags.Static);
field.SetValue(null, func);
var abc = Activator.CreateInstance(type);
var value = ((dynamic)abc).Execute("456");
Console.WriteLine($"static field: {value}");
}
}