防止DynamicMethod VerificationException-操作可能会使运行时不稳定 [英] prevent DynamicMethod VerificationException - operation could destabilize the runtime
问题描述
我正在使用IL生成来创建一个简单的反序列化方法,该方法将从Lucene文档中提取字符串,并设置引用类型对象(POCO)的属性或字段。
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值类型
尽管该方法将来会变得更加复杂,它现在只是在进行字符串分配。我尝试动态创建的方法将看起来像这样:
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)
感谢所有帮助!谢谢!
编辑:所以我简化了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 $ c做到了$ c> /
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屋!