gpt4 book ai didi

c# - 防止 DynamicMethod VerificationException - 操作可能会破坏运行时的稳定性

转载 作者:行者123 更新时间:2023-11-30 22:26:39 27 4
gpt4 key购买 nike

我正在使用 IL 生成创建一个简单的反序列化器方法,该方法从 Lucene 文档中提取字符串并设置引用类型对象 (POCO) 的属性或字段。

每当我尝试运行生成的方法时,我都会收到 VerificationException 错误。关于此错误还有其他问题,其中一些与 DynamicMethods 有关,但据我所知,我遇到的问题是不同的。

operation-could-destablize-the-runtime-and-dynamicmethod-with-value-types

msil-operation-could-destabilize-the-runtime-exception

虽然以后这个方法会变得更复杂,但我现在只是在做字符串赋值。我尝试动态创建的方法看起来完全像这样:

public static PocoObject ExampleMethod(Document document)
{
var poco = new PocoObject();
poco.ID = document.Get("ID");
poco.DisplayText = document.Get("DisplayText");

poco.PropId = document.Get("PropId");
return poco;
}

PocoObject 看起来像这样:

class PocoObject
{
public string ID;
public string DisplayText;

public string PropId { get; set; }

}

我试图完全复制从编译时代码生成的 IL(甚至是不必要的位),它看起来像这样:

.method public instance object  'Deserializebe6d500b-d35f-4f7a-a9b3-88f6bca5fb93'(class [Lucene.Net]Lucene.Net.Documents.Document A_1) cil managed
{
// Code size 65 (0x41)
.maxstack 4
IL_0000: nop
IL_0001: newobj instance void [LukeMapperTest]LukeMapperTest.PocoObject::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldarg.0
IL_0009: ldstr "ID"
IL_000e: callvirt instance string [Lucene.Net]Lucene.Net.Documents.Document::Get(string)
IL_0013: stfld string [LukeMapperTest]LukeMapperTest.PocoObject::ID
IL_0018: ldloc.0
IL_0019: ldarg.0
IL_001a: ldstr "DisplayText"
IL_001f: callvirt instance string [Lucene.Net]Lucene.Net.Documents.Document::Get(string)
IL_0024: stfld string [LukeMapperTest]LukeMapperTest.PocoObject::DisplayText
IL_0029: ldloc.0
IL_002a: ldarg.0
IL_002b: ldstr "PropId"
IL_0030: callvirt instance string [Lucene.Net]Lucene.Net.Documents.Document::Get(string)
IL_0035: callvirt instance void [LukeMapperTest]LukeMapperTest.PocoObject::set_PropId(string)
IL_003a: nop
IL_003b: ldloc.0
IL_003c: stloc.1
IL_003d: br.s IL_003f
IL_003f: ldloc.1
IL_0040: ret
} // end of method Test::'Deserializebe6d500b-d35f-4f7a-a9b3-88f6bca5fb93'

我已经设法将 DynamicMethod 保存到磁盘上的程序集,检查它,这正是它带来的。逐行与编译时方法 IL 相同。

然而,当执行动态方法时,会抛出上述错误。有谁知道我该如何解决这个问题?

注意:如果有人想仔细看看,我在 GitHub 上有源代码。 IL 生成代码在 LukeMapper.cs:GetDumbDeserializer()(第 133 行)

LukeMapper GitHub repo

感谢所有帮助!谢谢!


编辑:所以我简化了 IL 生成代码,它基本上如下:

private static Func<Document, object> GetDumbDeserializer(Type type)
{
var dm = new DynamicMethod(string.Format("Deserialize{0}", Guid.NewGuid()), typeof(object), new[] { typeof(Document) }, true);

var il = dm.GetILGenerator();

var ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
il.Emit(OpCodes.Nop);
il.DeclareLocal(type);
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Stloc_0);
Label returnLabel = il.DefineLabel();

//stack is [target]

var getFieldValue = typeof(Document).GetMethod("Get", BindingFlags.Instance | BindingFlags.Public);

foreach (var setter in settableProperties)
{
il.Emit(OpCodes.Ldloc_0);// [target]
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, setter.Name);
il.Emit(OpCodes.Callvirt, getFieldValue);
il.Emit(OpCodes.Stfld, setter.Field);
}

il.Emit(OpCodes.Nop);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Stloc_1); // stack is empty
il.Emit(OpCodes.Br_S, returnLabel);
il.MarkLabel(returnLabel);
il.Emit(OpCodes.Ldloc_1); // stack is [rval]
il.Emit(OpCodes.Ret);
return (Func<Document, object>)dm.CreateDelegate(typeof(Func<Document, object>));
}

我根据 kvbs 的评论添加了 il.DeclareLocal(type)(其中 type 是 PocoObject),但我不确定我是否将它放在正确的位置(或者它是否重要).

最佳答案

最后是乱码,用Stfld调用一个属性;我不知道最后的东西是从哪里来的,但我相信它来自 ExampleMethod - 我认为它可能来自您以前的构建。特别是,您还没有定义第二个本地,所以 Ldloc_1 没有意义;但这里绝对不需要任何标签/分支。这是我所拥有的,有效(注意我不知道你的 settableProperties 是什么,所以我只是使用 FieldInfo/PropertyInfo :

    var dm = new DynamicMethod(string.Format("Deserialize{0}", Guid.NewGuid()), typeof(object), new[] { typeof(Document) }, true);

var il = dm.GetILGenerator();

var ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
il.DeclareLocal(type);
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Stloc_0);

var getFieldValue = typeof(Document).GetMethod("Get", BindingFlags.Instance | BindingFlags.Public);

var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (var field in fields)
{
il.Emit(OpCodes.Ldloc_0);// [target]
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, field.Name);
il.Emit(OpCodes.Callvirt, getFieldValue);
il.Emit(OpCodes.Stfld, field);
}
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var prop in props)
{
var setter = prop.GetSetMethod();
if (setter == null) continue;
il.Emit(OpCodes.Ldloc_0);// [target]
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldstr, prop.Name);
il.Emit(OpCodes.Callvirt, getFieldValue);
il.EmitCall(OpCodes.Callvirt, setter, null);
}

il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ret);
return (Func<Document, object>)dm.CreateDelegate(typeof(Func<Document, object>));

为了比较,这是我在反射器(在发布版本等)中查看 ExampleMethod 时得到的结果:

.method public hidebysig static class PocoObject ExampleMethod(class Document document) cil managed
{
.maxstack 3
.locals init (
[0] class PocoObject poco)
L_0000: newobj instance void PocoObject::.ctor()
L_0005: stloc.0
L_0006: ldloc.0
L_0007: ldarg.0
L_0008: ldstr "ID"
L_000d: callvirt instance string Document::Get(string)
L_0012: stfld string PocoObject::ID
L_0017: ldloc.0
L_0018: ldarg.0
L_0019: ldstr "DisplayText"
L_001e: callvirt instance string Document::Get(string)
L_0023: stfld string PocoObject::DisplayText
L_0028: ldloc.0
L_0029: ldarg.0
L_002a: ldstr "PropId"
L_002f: callvirt instance string Document::Get(string)
L_0034: callvirt instance void PocoObject::set_PropId(string)
L_0039: ldloc.0
L_003a: ret
}

注意事项:

  • 没有标签/分支(我不知道那是从哪里来的,但那不是你发布的)
  • 它定义了一个本地
  • 没有 nop

关于c# - 防止 DynamicMethod VerificationException - 操作可能会破坏运行时的稳定性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11680490/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com