I'm generating some code via Cecil. The code generates without error, but when I try to load the assembly I get:
An unhandled exception of type 'System.InvalidProgramException' occurred in DataSerializersTest.exe
Additional information: Common Language Runtime detected an invalid program.
Here is the generated IL:
.method public static
class Data.FooData Read (
class [mscorlib]System.IO.BinaryReader input
) cil managed
{
// Method begins at RVA 0x3a58
// Code size 60 (0x3c)
.maxstack 3
.locals (
[0] class Data.FooData,
[1] valuetype [System.Runtime]System.Nullable`1<int32>
)
IL_0000: newobj instance void Data.FooData::.ctor()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldarg.0
IL_0008: callvirt instance bool [System.IO]System.IO.BinaryReader::ReadBoolean()
IL_000d: ldc.i4.0
IL_000e: ceq
IL_0010: brtrue.s IL_001f
IL_0012: ldarg.0
IL_0013: callvirt instance int32 [System.IO]System.IO.BinaryReader::ReadInt32()
IL_0018: newobj instance void valuetype [System.Runtime]System.Nullable`1<int32>::.ctor(!0)
IL_001d: br.s IL_0028
IL_001f: nop
IL_0020: ldloca.s 1
IL_0022: initobj valuetype [System.Runtime]System.Nullable`1<int32>
IL_0028: nop
IL_0029: callvirt instance void Data.FooData::set_I(valuetype [System.Runtime]System.Nullable`1<int32>)
IL_002e: ldloc.0
IL_002f: ldarg.0
IL_0030: callvirt instance string [System.IO]System.IO.BinaryReader::ReadString()
IL_0035: callvirt instance void Data.FooData::set_Name(string)
IL_003a: ldloc.0
IL_003b: ret
} // end of method FooDataReader::Read
The equivalent C# code looks like this:
public static FooData Read(BinaryReader input)
{
var result = new FooData();
if (input.ReadBoolean())
{
result.I = input.ReadInt32();
}
else
{
result.I = null;
}
result.Name = input.ReadString();
return result;
}
I've worked through the IL several times and it makes sense to me. It's not the same as what the C# compiler produces for the above C# code, but I'd like to understand what's wrong with the IL I've generated. Can anyone tell me?
I think I've spotted it. initobj
does not leave the newly initialized object on the evaluation stack - whereas newobj
does. So the stacks are unbalanced when IL_0028
is reached - if we've gone the newobj
route, then we have two items on the stack (FooData
and Nullable<int>
). If we've gone the initobj
route, then all we have is the FooData
object reference on the stack. You need to reload local 1
:
IL_0022: initobj valuetype [System.Runtime]System.Nullable`1<int32>
ldloc.1
IL_0028: nop
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With