IL Emit 结构序列化器
IL Emit struct serializer
我正在编写将任何结构编组为字节数组的代码。我有一个方法:
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;
}
这是此代码的 IL:
.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
现在我正在尝试发出通用方法:
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[]>));
}
}
但是当我尝试调用它时失败并显示 CLR detected an invalid program
。我认为问题出在这一行:
generator.Emit(OpCodes.Ldarga_S, parameterName);
但如果我写:
generator.Emit(OpCodes.Ldarga_S, 0);
如果失败 NullReferenceException
现在我有一段代码可以使用泛型 T 但它使用了未记录的关键字
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;
}
它不稳定,可能会在新的 .Net 版本中崩溃,所以我想用基于 Emit 的代码替换它
有一些问题。首先,您将 parameterName
用于 Ldarga_S
是不正确的,应该是 (byte)0
。请注意,需要 byte
强制转换以确保您调用正确的 Emit()
重载 - Ldarga_S
采用 byte
参数,但如果没有强制转换,您将调用重载使用 int
作为它的第二个参数,这在这个例子中实际上并不是一个主要问题,尽管你会在你的输出 IL 中得到 nop
s - 你的 Ldc_I4_S
也存在类似的问题,它采用 sbyte
参数。
您的局部变量也被错误定义 - 您将它们定义为 typeof(byte)
而它们应该是 typeof(byte[])
.
纠正这些问题后,它似乎可以按预期工作。
你把事情搞得太复杂了。这部分
byte[] result = new byte[12];
Marshal.Copy(p, result, 0, result.Length);
return result;
根本不需要发射。您只需要发出指针操作,这应该是非常简单的指令。仅发射:
Serialize(new IntPtr(&value), restOfArgs);
其中Serialize
是从post开始的三行。
我正在编写将任何结构编组为字节数组的代码。我有一个方法:
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;
}
这是此代码的 IL:
.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
现在我正在尝试发出通用方法:
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[]>));
}
}
但是当我尝试调用它时失败并显示 CLR detected an invalid program
。我认为问题出在这一行:
generator.Emit(OpCodes.Ldarga_S, parameterName);
但如果我写:
generator.Emit(OpCodes.Ldarga_S, 0);
如果失败 NullReferenceException
现在我有一段代码可以使用泛型 T 但它使用了未记录的关键字
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;
}
它不稳定,可能会在新的 .Net 版本中崩溃,所以我想用基于 Emit 的代码替换它
有一些问题。首先,您将 parameterName
用于 Ldarga_S
是不正确的,应该是 (byte)0
。请注意,需要 byte
强制转换以确保您调用正确的 Emit()
重载 - Ldarga_S
采用 byte
参数,但如果没有强制转换,您将调用重载使用 int
作为它的第二个参数,这在这个例子中实际上并不是一个主要问题,尽管你会在你的输出 IL 中得到 nop
s - 你的 Ldc_I4_S
也存在类似的问题,它采用 sbyte
参数。
您的局部变量也被错误定义 - 您将它们定义为 typeof(byte)
而它们应该是 typeof(byte[])
.
纠正这些问题后,它似乎可以按预期工作。
你把事情搞得太复杂了。这部分
byte[] result = new byte[12];
Marshal.Copy(p, result, 0, result.Length);
return result;
根本不需要发射。您只需要发出指针操作,这应该是非常简单的指令。仅发射:
Serialize(new IntPtr(&value), restOfArgs);
其中Serialize
是从post开始的三行。