DynamicMethod call Instance method - c#

If I create a DynamicMethod from inside a class method how can I call another method of my class from the DynamicMethod Delegate? I need somehow to capture the this reference in the DynamicMethod code.
But I can't find an overladed version of ILGenerator.Emit which takes an object as parameter.
The code is so far:
void CallOpc(string name, object[] inp, object[] outp)
{
//...
}
public D CreateDelegate<D>(string opcName) where D : class
{
var dType = typeof(D);
var invoke = dType.GetMethod("Invoke");
var parameters = invoke.GetParameters();
var paramTypes = parameters.Select(p => p.ParameterType).ToArray();
DynamicMethod dm = new DynamicMethod(
opcName,
invoke.ReturnType,
paramTypes,
true);
var inp = parameters.Where(p => !p.IsOut).Select(p => p.ParameterType).ToList();
var outp = parameters.Where(p => p.IsOut).Select(p => p.ParameterType).ToList();
if (invoke.ReturnType != typeof(void))
{
outp.Insert(0, invoke.ReturnType);
}
ILGenerator il = dm.GetILGenerator();
LocalBuilder invar = il.DeclareLocal(typeof(object[]));
LocalBuilder outvar = il.DeclareLocal(typeof(object[]));
il.Emit(OpCodes.Ldc_I4, inp.Count);
il.Emit(OpCodes.Newarr, typeof(object));
il.Emit(OpCodes.Stloc, invar);
for (int i = 0; i < inp.Count; i++)
{
il.Emit(OpCodes.Ldloc, invar);
il.Emit(OpCodes.Ldc_I4, i);
int j = Array.IndexOf(paramTypes, inp[i]);
il.Emit(OpCodes.Ldarg, j);
if (!inp[i].IsClass)
{
il.Emit(OpCodes.Box, inp[i]);
}
il.Emit(OpCodes.Stelem_Ref);
}
il.Emit(OpCodes.Ldc_I4, outp.Count);
il.Emit(OpCodes.Newarr, typeof(object));
il.Emit(OpCodes.Stloc, outvar);
il.Emit(OpCodes.Ldarg_0); // <- push this on the evaluation stack ???
il.Emit(OpCodes.Ldstr, opcName);
il.Emit(OpCodes.Ldloc, invar);
il.Emit(OpCodes.Ldloc, outvar);
MethodInfo callOpcMeth = GetType().GetMethod("CallOpc", BindingFlags.Instance | BindingFlags.NonPublic);
il.Emit(OpCodes.Callvirt, callOpcMeth);
for (int o = 0; o < outp.Count; o++)
{
// TODO: handle out params and return value
}
il.Emit(OpCodes.Ret);
return (D)(object)dm.CreateDelegate(dType);
}
My problem is the line marked with ???

How to reference the this pointer from a DynamicMethod?
At low level, methods do not have a specific this pointer, instead the first argument of the method is used as this.
To reference the this pointer, here is what you have to do:
Prepend a new argument to your argument list, matching the type you want to use as this
Use that argument as this wherever you want
Create the delegate using DynamicMethod.CreateDelegate(Type,Object) to bind the first parameter to your object: return (D)(object)dm.CreateDelegate(dType, this);
Note that creating dynamic methods is expensive. If you generate the same DynamicMethod for multiple instances, you should cache the method itself, and create delegates using the cached method, but with different target parameter.

Related

InvalidProgramException: Invalid IL code happens when emitting IL code with non-void return type

I am trying to lower down reflection method call time by creating a DynamicMethod, emitting IL code with its IL generator, and then creating a delegate using its CreateDelegate method. So far the call times are reduced significantly even though the new method call still uses (object callObject, object[] params) as parameters.
The problem occurs in the generated IL code when the return type is anything other than void. I have no knowledge of MSIL and did quite a bit of research but found nothing. Here is the code that works:
private void Start()
{
var a = new A();
var method = typeof(A).GetMethod("Add");
Type[] paramsTypes = { typeof(object[]) };
Type[] allTypes = { typeof(object), typeof(object[]) };
Type returnType = typeof(void);
var dm = new DynamicMethod("Hi", returnType, allTypes);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, method, paramsTypes);
il.Emit(OpCodes.Ret);
var del4 = (Action<object, object[]>)dm.CreateDelegate(
typeof(Action<object, object[]>));
var time = DateTime.Now;
for (int i = 0; i < 20000000; i++)
{
//a.Add(); 132 ms
//method.Invoke(a, null);// 25 sec
del4(a, null); // 200 ms
}
var ts = DateTime.Now - time;
Debug.Log($"{ts.Seconds}:{ts.Milliseconds}");
}
public class A
{
public int a = 0;
public void Add() => a++;
}
The resulting time (only the call time is measured) is so much faster compared to normal MethodInfo.Invoke. However, with the following tweaks, the IL generator throws error:
private void Start()
{
var a = new A();
var method = typeof(A).GetMethod("Add");
Type[] paramsTypes = { typeof(object[]) };
Type[] allTypes = { typeof(object), typeof(object[]) };
Type returnType = typeof(object);
var dm = new DynamicMethod("Hi", returnType, allTypes);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, method, paramsTypes);
il.Emit(OpCodes.Ret);
var del4 = (Func<object, object[], object>)dm.CreateDelegate(
typeof(Func<object, object[], object>));
var time = DateTime.Now;
for (int i = 0; i < 20000000; i++)
{
//a.Add(); 132 ms
//method.Invoke(a, null);// 25 sec
del4(a, null); // 200 ms
}
var ts = DateTime.Now - time;
Debug.Log($"{ts.Seconds}:{ts.Milliseconds}");
}
public class A
{
public int a = 0;
public int Add() => a++;
}
When the return type expected is something other than void, the following exception is thrown:
InvalidProgramException: Invalid IL code in (wrapper dynamic-method) object:Hi (object,object[]): IL_0006: ret
Does anyone know how to solve this issue?
Edit: Added boxing before return opcode:
il.Emit(OpCodes.Ldarg_0);
il.EmitCall(OpCodes.Call, method, paramsTypes);
il.Emit(OpCodes.Box);
il.Emit(OpCodes.Ret);
Then the following is thrown:
VerificationException: Error in System.Object:(wrapper dynamic-method) object:Hi (object,object[]) Invalid instruction 8c

Call method via DynamicMethod - Reflection.Emit

I've got a slightly modified class of this answer in order to dynamically call TryParse of various types (char, int, long).
public delegate TRet DynamicMethodDelegate<TRet>(object target, params object[] args);
public delegate void DynamicMethodDelegate(object target, params object[] args);
public class DynamicMethodDelegateFactory
{
public static TDelegate CreateMethodCaller<TDelegate>(MethodInfo method)
where TDelegate : class
{
ParameterInfo[] parameters = method.GetParameters();
Type[] args = { typeof(object), typeof(object[]) };
DynamicMethod dynam =
new DynamicMethod
(
method.Name
, method.ReturnType
, args
, typeof(DynamicMethodDelegateFactory)
, true
);
//Add parmeter attributes to the new method from the existing method
for (int i = 0; i < parameters.Length; i++)
{
dynam.DefineParameter
(
i,
parameters[i].Attributes,
parameters[i].Name
);
}
ILGenerator il = dynam.GetILGenerator();
// If method isn't static push target instance on top of stack.
if (!method.IsStatic)
{
// Argument 0 of dynamic method is target instance.
il.Emit(OpCodes.Ldarg_0);
}
// Lay out args array onto stack.
LocalBuilder[] locals = new LocalBuilder[parameters.Length];
List<LocalBuilder> outOrRefLocals = new List<LocalBuilder>();
for (int i = 0; i < parameters.Length; i++)
{
//Push args array reference onto the stack, followed
//by the current argument index (i). The Ldelem_Ref opcode
//will resolve them to args[i].
if (!parameters[i].IsOut)
{
// Argument 1 of dynamic method is argument array.
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldelem_Ref);
}
// If parameter [i] is a value type perform an unboxing.
Type parameterType = parameters[i].ParameterType;
if (parameterType.IsValueType)
{
il.Emit(OpCodes.Unbox_Any, parameterType);
}
}
//Create locals for out parameters
for (int i = 0; i < parameters.Length; i++)
{
if (parameters[i].IsOut)
{
locals[i] = il.DeclareLocal(parameters[i].ParameterType.GetElementType());
il.Emit(OpCodes.Ldloca, locals[locals.Length - 1]);
}
}
if (method.IsFinal || !method.IsVirtual)
{
il.Emit(OpCodes.Call, method);
}
else
{
il.Emit(OpCodes.Callvirt, method);
}
for (int idx = 0; idx < parameters.Length; ++idx)
{
if (parameters[idx].IsOut || parameters[idx].ParameterType.IsByRef)
{
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4, idx);
il.Emit(OpCodes.Ldloc, locals[idx].LocalIndex);
if (parameters[idx].ParameterType.GetElementType().IsValueType)
il.Emit(OpCodes.Box, parameters[idx].ParameterType.GetElementType());
il.Emit(OpCodes.Stelem_Ref);
}
}
if (method.ReturnType != typeof(void))
{
// If result is of value type it needs to be boxed
if (method.ReturnType.IsValueType)
{
il.Emit(OpCodes.Box, method.ReturnType);
}
}
else
{
il.Emit(OpCodes.Ldnull);
}
il.Emit(OpCodes.Ret);
return dynam.CreateDelegate(typeof(TDelegate)) as TDelegate;
}
}
Unfortunately this throws an AccessViolationException and after checking the code a bit more in detail I'm still unsure why.
This code also "runs" in another project where it seems like the return value is not consistent.
Sometimes it just returns false when the actual parsing via TryParse is successful.
It sounds like undefined behaviour, but I can't seem to find the issue(s).
Here's a sample code of the AccessViolationException AND the undefined behaviour (remove float value from array for UB).
static void Main(string[] args)
{
var arr = new object[] { 'A', (byte)1, (short)2, 3, 4L, 5M, 6.0, 7.0F, "8" };
for (int i = 0; i < 100000; i++)
{
foreach (var item in arr)
ParseTest(item);
}
int a = 1;
int b = a;
}
static Type StringType = typeof(string);
static bool ParseTest(object data)
{
var type = data.GetType();
if (type == StringType)
return true;
else
{
var mi = type.GetMethod(nameof(int.TryParse), new[] { StringType, type.MakeByRefType() });
var dmd = DynamicMethodDelegateFactory.CreateMethodCaller<DynamicMethodDelegate<bool> >(mi);
dynamic dummy = null;
var args = new object[] { data, dummy };
var ok = dmd(null, args);
return ok;
}
}
What is the problem of the UB ?
Why the AccessViolationException ? Another problem or related to UB ?

Compiled Expression slower than Reflection

I have a PropertyInfo.SetValue that has a dynamic set. Meaning the value to be set is not known.
I've have a method like this i got from the internet.
private static Action<object, object> CreateSetAccess(MethodInfo method)
{
var obj = Expression.Parameter(typeof(object), "o");
var value = Expression.Parameter(typeof(object));
Expression<Action<object, object>> expr =
Expression.Lambda<Action<object, object>>(
Expression.Call(
Expression.Convert(obj, method.DeclaringType),
method,
Expression.Convert(value, method.GetParameters()[0].ParameterType)),
obj,
value);
return expr.Compile();
}
What this does is create a expression and compile it, but the objects get converted using the parameter types.
I consume it like this.
var method2 = CreateSetAccess(property.GetSetMethod());
method2(response, valueToSet);
What happens is that this seems to be slower that the PropertyInfo.SetValue
Here is my benchmark
var xpathNavigator = XmlHelper.CreateXPathDocument(serviceResponse).CreateNavigator();
foreach (var propertyInformation in propertyInformationSource)
{
// Gets the node using the NodePath provided in the Attribute
var attr = propertyInformation.Value;
var pathValue = xpathNavigator.SelectSingleNode(attr.NodePath);
if (pathValue == null)
continue;
object valueToSet = null;
var property = propertyInformation.Key;
if (propertyInformation.Value.ShouldDeserialize)
valueToSet = serializationHelper.Deserialize(property.PropertyType, pathValue.OuterXml, attr.CustomRoot);
else
valueToSet = Convert.ChangeType(pathValue.Value, property.PropertyType);
// this line is only added for the testing for it to be JITd
var method = CreateSetAccess(property.GetSetMethod());
method(response, valueToSet);
property.SetValue(response, valueToSet);
// end
TimeSpan fastSet, setValue;
const int COUNT = 100000;
var watch = Stopwatch.StartNew();
for (int i = 0; i < COUNT; i++)
{
var method2 = CreateSetAccess(property.GetSetMethod());
method2(response, valueToSet);
}
watch.Stop();
fastSet = watch.Elapsed; // result {00:00:08.8500760}
watch = Stopwatch.StartNew();
for (int i = 0; i < COUNT; i++)
{
property.SetValue(response, valueToSet);
}
watch.Stop();
setValue = watch.Elapsed; // result {00:00:00.0263953}
}
I'm wondering why this happens? I'm guessing because I'm always creating a new expression, but how can i make it not create a new object and make it get cached?
If compiling a fresh expression each time was faster then the Reflection API would just do this internally. Therefore, it is not. This technique only works if you reuse the same compiled code many times.
so there is no way to make the expression adjust at runtime based on the methodinfo supplied?
Reflection does this and that's what's making it so slow. Keep a cache of compiled methods. For example in a Dictionary<MethodInfo, Action<object, object>> with a suitable comparer.
Create Dictionary<MethodInfo, Action<object, object>> actions field in your class.
You can remove the static modifier from CreateSetAccess. And then add the following code in it:
Action<object, object> action = null;
if(actions.TryGetValue(method, out action))
{
return action;
}
var obj = Expression.Parameter(typeof(object), "o");
var value = Expression.Parameter(typeof(object));
Expression<Action<object, object>> expr =
Expression.Lambda<Action<object, object>>(
Expression.Call(
Expression.Convert(obj, method.DeclaringType),
method,
Expression.Convert(value, method.GetParameters()[0].ParameterType)),
obj,
value);
expr.Compile();
cache.Add(method, expr);
return expr;
}

Reflection.Emit create object with parameters

I'm creating a dynamic function to create an object at runtime given an object[] of constructor params. I keep getting the generic exception 'Operation could destablise the runtime' and I can't see what I've done wrong.
The method works fine if the created object needs no constructor arguments - so the problem must be in the code in the for loop.
The code indexes into the given object[] putting the object onto the stack after which the ctor is called and the object returned.
Any ideas???
internal static Func<object[], object> CreateObjectFactoryMethodWithCtorParams(ConstructorInfo ctor, int ctorArgsLength)
{
Func<object[], object> factoryMethod = null;
if (ctor != null)
{
var dm = new DynamicMethod(string.Format("_CreationFacotry_{0}", Guid.NewGuid()), typeof(object), new Type[] { typeof(object[])}, true);
var il = dm.GetILGenerator();
il.DeclareLocal(typeof(int));
il.DeclareLocal(typeof(object));
il.BeginExceptionBlock();
il.Emit(OpCodes.Ldc_I4_0); // [0]
il.Emit(OpCodes.Stloc_0); //[nothing]
for (int i = 0; i < ctorArgsLength; i++)
{
EmitInt32(il, i); // [args][index]
il.Emit(OpCodes.Stloc_0); // [args][index]
il.Emit(OpCodes.Ldarg_0); //[args]
EmitInt32(il, i); // [args][index]
il.Emit(OpCodes.Ldelem_Ref); // [item-in-args-at-index]
}
il.Emit(OpCodes.Newobj, ctor); //[new-object]
il.Emit(OpCodes.Stloc_1); // nothing
il.BeginCatchBlock(ExceptionType); // stack is Exception
il.Emit(OpCodes.Ldloc_0); // stack is Exception, index
il.EmitCall(OpCodes.Call, EmitGeneratorType.GetMethod("ThrowFactoryException"), null);
il.EndExceptionBlock();
il.Emit(OpCodes.Ldloc_1); //[new-object]
il.Emit(OpCodes.Ret);
factoryMethod = (Func<object[], object>)dm.CreateDelegate(typeof(Func<object[], object>));
}
else
{
throw new EmitGeneratorException("Cannot create instance factory for a null ctor instance");
}
return factoryMethod;
}
private static void EmitInt32(ILGenerator il, int value)
{
switch (value)
{
case -1: il.Emit(OpCodes.Ldc_I4_M1); break;
case 0: il.Emit(OpCodes.Ldc_I4_0); break;
case 1: il.Emit(OpCodes.Ldc_I4_1); break;
case 2: il.Emit(OpCodes.Ldc_I4_2); break;
case 3: il.Emit(OpCodes.Ldc_I4_3); break;
case 4: il.Emit(OpCodes.Ldc_I4_4); break;
case 5: il.Emit(OpCodes.Ldc_I4_5); break;
case 6: il.Emit(OpCodes.Ldc_I4_6); break;
case 7: il.Emit(OpCodes.Ldc_I4_7); break;
case 8: il.Emit(OpCodes.Ldc_I4_8); break;
default:
if (value >= -128 && value <= 127)
{
il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
}
else
{
il.Emit(OpCodes.Ldc_I4, value);
}
break;
}
}
Calling code
Func<object[], object> factoryFunction = GetFunction(someCtor, new object[] { arg1, arg2});
var obj = factoryFunction(new object[] {new SomeClass, "A String" }); //input ctor args
It works fine for me, as long as I make all the constructor parameters object:
class SomeClass {
public SomeClass(object s, object t) { }
}
static void Main()
{
var someCtor = typeof(SomeClass).GetConstructors()[0];
Func<object[], object> factoryFunction = CreateObjectFactoryMethodWithCtorParams(someCtor, someCtor.GetParameters().Length);
var obj = factoryFunction(new object[] {"A String", 123 });
}
I think the problem is that you haven't done any conversions from the objects from the array to the actual constructor types, noting that you need to consider both reference types and value-types (unbox). Like so:
var parameters = ctor.GetParameters();
for (int i = 0; i < parameters.Length ; i++)
{
EmitInt32(il, i); // [index]
il.Emit(OpCodes.Stloc_0); // [nothing]
il.Emit(OpCodes.Ldarg_0); //[args]
EmitInt32(il, i); // [args][index]
il.Emit(OpCodes.Ldelem_Ref); // [item-in-args-at-index]
var paramType = parameters[i].ParameterType;
if (paramType != typeof(object))
{
il.Emit(OpCodes.Unbox_Any, paramType); // same as a cast if ref-type
}
}
il.Emit(OpCodes.Newobj, ctor); //[new-object]
il.Emit(OpCodes.Stloc_1); // nothing
as a minor note: since you need to call .GetParameters(), you should not pass in the parameter length as a parameter to the method; that is redundant, and could cause errors when wrong.
This then works with my exmaple:
class SomeClass {
public SomeClass(string s, int t) { }
}
static void Main()
{
var someCtor = typeof(SomeClass).GetConstructors()[0];
Func<object[], object> factoryFunction = CreateObjectFactoryMethodWithCtorParams(someCtor);
var obj = factoryFunction(new object[] {"A String", 123 });
}

How to get the ParameterInfo of a function with variable number of params?

How to get the ParameterInfo of a function with variable number of params?
The problem is when I call the method
MyFunction(object o1, out object o2);
I can get the parameterInfo of sendData but not the o1 and o2 object.
protected object[] MyFunction(params object[] sendData)
{
StackTrace callStack = new StackTrace(0, false);
StackFrame callingMethodFrame = callStack.GetFrame(0);
MethodBase callingMethod = callingMethodFrame.GetMethod();
ParameterInfo[] parametersInfo = callingMethod.GetParameters();
List<object> inParams = new List<object>();
List<object> outParams = new List<object>();
for (int i = 0; i < sendData.Length; i++)
{
object value = sendData[i];
ParameterInfo info = parametersInfo[parametersInfo.Length - sendData.Length + i];
if (info.IsOut)
{
outParams.Add(value);
}
else
{
inParams.Add(value);
}
}
..........
}
Thanks in advance for helping me.
Arnaud
'params' is just C# syntactic sugar. In fact, at metadata .NET level, there is only one parameter named "sendData" with a specific "ParamArray" attribute set.

Categories

Resources