防止DynamicMethod VerificationException-操作可能会使运行时不稳定 [英] prevent DynamicMethod VerificationException - operation could destabilize the runtime

查看:96
本文介绍了防止DynamicMethod VerificationException-操作可能会使运行时不稳定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using IL generation to create a simple deserializer method which takes strings out of a Lucene document and sets properties or fields of a reference-type object (POCO).

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

Whenever I try to run the generated method, I receive a VerificationException error. There are other questions regarding this error, a few of which have to do with DynamicMethods, but from what I can tell the issue I am having is different.

operation-could-destablize-the-runtime-and-dynamicmethod-with值类型

msil操作可能会破坏运行时异常

尽管该方法将来会变得更加复杂,它现在只是在进行字符串分配。我尝试动态创建的方法将看起来像这样:

Although the method will get more complicated in the future, I have it just doing string assignment right now. The method I am trying to create dynamically will look exactly like this:

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如下所示:

Where PocoObject looks like this:

class PocoObject
{
    public string ID;
    public string DisplayText;

    public string PropId { get; set; }

}

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

I tried to replicate the IL generated from the compile-time code EXACTLY (even the unnecessary bits) and it looks like this:

.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'

I已经设法将DynamicMethod保存到磁盘上的程序集中,对其进行了检查,这正是它所带来的。一行一行与编译时方法IL相同。

I have managed to save the DynamicMethod to an assembly on disk, inspected it, and this is exactly what it brings up. Line for line it is the same as the compile-time method IL.

尽管如此,在执行动态方法时,会引发上述错误。有没有人知道如何解决这个问题?

Nevertheless, when executing the dynamic method, the error above is thrown. Does anyone have any idea how I could fix this?

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

Note: I have the source code on GitHub if anyone would like to take a closer look. The IL Generation code is in LukeMapper.cs:GetDumbDeserializer() (line 133)

LukeMapper GitHub存储库

感谢所有帮助!谢谢!

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

So I have simplified the IL generation code and it is essentially the following:

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>));
}

我在 il.DeclareLocal(type)中添加了(其中类型为PocoObject)每个kvbs的注释,但是我不确定是否将其放在正确的位置(或是否重要)。

I added in the il.DeclareLocal(type) (where type is PocoObject) per kvbs's comment, but I'm not sure if I am putting it in the right place (or if it matters).

推荐答案

最后是胡言乱语,并使用 Stfld 调用属性;我不知道最终结果来自哪里,但我相信它来自 ExampleMethod -我认为这可能是以前的建立你的。特别是,您尚未定义第二个本地变量,因此 Ldloc_1 没有任何意义;但这里绝对不需要任何标签/分支。这就是我的工作原理(请注意,我不知道您的 settableProperties 是什么,所以我只是使用 FieldInfo / PropertyInfo

It is the gibberish at the end, and using Stfld to call a property; I have no idea where that end stuff came from, but I do not believe that came from ExampleMethod - I think that might have been from a previous build of yours. In particular, you haven't defined a second local, so Ldloc_1 makes no sense; but there is absolutely no need for any labels / branches here. Here is what I have, that works (note I didn't know what your settableProperties were, so I did it just using 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 在反射器中(在发行版本中):

And for comparison purposes, here is what I get when looking at ExampleMethod in reflector (in a release build, etc):

.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

  • no labels / branching (I have no idea where that came from, but that is not from what you posted)
  • it defines a local
  • no nop

这篇关于防止DynamicMethod VerificationException-操作可能会使运行时不稳定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆