I'm writing a code that marshal any structure to byte array. I have a method:
public static byte[] Serialize(MyStruct value)
{
IntPtr p = new IntPtr(&value);
byte[] result = new byte[12];
Marshal.Copy(p, result, 0, result.Length);
return result;
}
Here is IL for this code:
.method public hidebysig static uint8[] Serialize(valuetype Program/MyStruct 'value') cil managed
{
// code size: 37 (0x25)
.maxstack 4
.locals init ([0] native int p,
[1] uint8[] result,
[2] uint8[] V_2)
IL_0000: nop
IL_0001: ldloca.s p
IL_0003: ldarga.s 'value'
IL_0005: conv.u
IL_0006: call instance void [mscorlib]System.IntPtr::.ctor(void*)
IL_000b: ldc.i4.s 12
IL_000d: newarr [mscorlib]System.Byte
IL_0012: stloc.1
IL_0013: ldloc.0
IL_0014: ldloc.1
IL_0015: ldc.i4.0
IL_0016: ldloc.1
IL_0017: ldlen
IL_0018: conv.i4
IL_0019: call void [mscorlib]System.Runtime.InteropServices.Marshal::Copy(native int,
uint8[],
int32,
int32)
IL_001e: nop
IL_001f: ldloc.1
IL_0020: stloc.2
IL_0021: br.s IL_0023
IL_0023: ldloc.2
IL_0024: ret
} // end of method MyStruct::Serialize
now I'm trying to emit generic method:
private static class SerializationHolder<T> where T : struct
{
public static readonly Func<T, byte[]> Value = CreateDelegate();
private static Func<T, byte[]> CreateDelegate()
{
var dm = new DynamicMethod("Serialize" + typeof (T).Name,
typeof (byte[]),
new[] {typeof (T)},
Assembly.GetExecutingAssembly().ManifestModule);
const string parameterName = "value";
dm.DefineParameter(1, ParameterAttributes.None, parameterName);
var generator = dm.GetILGenerator();
var p = generator.DeclareLocal(typeof (IntPtr));
generator.DeclareLocal(typeof (byte));
generator.DeclareLocal(typeof (byte));
generator.Emit(OpCodes.Ldloca_S, p);
generator.Emit(OpCodes.Ldarga_S, parameterName);
generator.Emit(OpCodes.Conv_U);
var intPtrCtor = typeof (IntPtr).GetConstructor(new[] {typeof(void*)});
Debug.Assert(intPtrCtor != null);
generator.Emit(OpCodes.Call, intPtrCtor);
var sizeInBytes = Marshal.SizeOf(typeof (T));
generator.Emit(OpCodes.Ldc_I4_S, sizeInBytes);
generator.Emit(OpCodes.Newarr, typeof (byte));
generator.Emit(OpCodes.Stloc_1);
generator.Emit(OpCodes.Ldloc_0);
generator.Emit(OpCodes.Ldloc_1);
generator.Emit(OpCodes.Ldc_I4_0);
generator.Emit(OpCodes.Ldloc_1);
generator.Emit(OpCodes.Ldlen);
generator.Emit(OpCodes.Conv_I4);
var marshalCopy = typeof (Marshal).GetMethod("Copy", new[] {typeof (IntPtr), typeof (byte[]), typeof (int), typeof (int)});
generator.EmitCall(OpCodes.Call, marshalCopy, null);
generator.Emit(OpCodes.Ldloc_1);
generator.Emit(OpCodes.Stloc_2);
generator.Emit(OpCodes.Ldloc_2);
generator.Emit(OpCodes.Ret);
return (Func<T, byte[]>)dm.CreateDelegate(typeof(Func<T, byte[]>));
}
}
but when I'm trying to call it it fails with CLR detected an invalid program
. I think the problem is in this line:
generator.Emit(OpCodes.Ldarga_S, parameterName);
but if I'm writing:
generator.Emit(OpCodes.Ldarga_S, 0);
if fails with NullReferenceException
Now I have a code that do it with generic type T but it uses undocumented keywords
public static byte[] Serialize<T>(this T value) where T : struct
{
TypedReference tr = __makeref(value);
IntPtr p = *(IntPtr*)&tr;
int sizeInBytes = Marshal.SizeOf(typeof(T));
byte[] result = new byte[sizeInBytes];
Marshal.Copy(p, result, 0, result.Length);
return result;
}
It's unstable and can break down in new .Net releases so I want to replace it with Emit-based code