20.9.08

Generating C# at Runtime: Casting Your Args

I spent the morning (like so many morning before this one), trying to debug some Reflection.Emit code. Naturally, the actual implementation is so much more complicated than this example I whipped up, which explains why it took so long to isolate the issue. Take a look at this sample code:

DynamicMethod m = new DynamicMethod("Demo", typeof(double), 
new Type[] { typeof(double[]) }, typeof(Program), false);
ILGenerator il = m.GetILGenerator();

il.Emit(OpCodes.Ldc_R8, 1.0);
il.Emit(OpCodes.Ldc_R8, 1); // <---
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Ret);

Func del = (Func)m.CreateDelegate(typeof(Func));
Console.WriteLine("Result: " + del.Invoke());


When executed, the dynamically-compiled delegate throws an InvalidProgramException, with the message, "Common Language Runtime detected an invalid program." The problem here is that the literal 1 is not actually a 64-bit floating-point number. This can be quickly rectified by either casting 1 as a double, or using type specifiers such as 1.0 or 1D.

That's basically the moral of this story. Perhaps Emit is at fault, in that it doesn't check or typecast arguments for you, but until that gets fixed, if ever, make sure you type your arguments correctly!

I was further surprised while debugging this issue, when it allowed to me to change the Ldc_R8 to Ldc_I4, and keep the value of 1. According to the documentation on .Add, adding floats and integers is not allowed and SHOULD throw an exception. However, it does not.

il.Emit(OpCodes.Ldc_I4, 1);
il.Emit(OpCodes.Ldc_R8, 1D);


Perhaps there is another explaination for this, or perhaps the documentation is just plain incorrect.

No comments: