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

时间:2012-07-27 01:59:52

标签: c# cil dynamicmethod

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

每当我尝试运行生成的方法时,都会收到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),但我不确定我是否将它放在正确的位置(或者是否重要)。

0 个答案:

没有答案