evitar DynamicMethod VerificationException - operação poderia desestabilizar o tempo de execução
-
12-12-2019 - |
Pergunta
Eu estou usando o IL geração para criar uma simples desserializador método que leva as cordas de um documento Lucene e conjuntos de propriedades ou campos de uma referência do tipo de objeto (POCO).
Sempre que eu tento executar o gerado método, recebo um VerificationException erro.Há outras questões em relação a este erro, algumas das quais têm a ver com DynamicMethods, mas do que posso dizer, o problema que eu estou tendo é diferente.
operação-pode-destablize-o-tempo de execução-e-dynamicmethod-com-valor-tipos de
msil-operação-poderia desestabilizar-o-tempo de execução-exceção de
Embora o método que vai ficar mais complicado no futuro, eu tenho que fazer apenas atribuição de seqüência de caracteres para a direita agora.O método que eu estou tentando criar dinamicamente, será exatamente assim:
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;
}
Onde PocoObject parecido com este:
class PocoObject
{
public string ID;
public string DisplayText;
public string PropId { get; set; }
}
Eu tentei replicar o IL gerado a partir da compilação de código de tempo EXATAMENTE (até mesmo desnecessária bits) e ele se parece com isso:
.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'
Eu consegui salvar o DynamicMethod para uma assembleia no disco, inspecionou, e isso é exatamente o que ele traz.Linha por linha, é o mesmo que o de tempo de compilação método IL.
No entanto, quando executar o método dinâmico, o erro acima é lançada.Alguém tem alguma idéia de como eu poderia resolver isso?
Nota:Eu tenho o código fonte no GitHub, se alguém quiser dar uma olhada.O IL Geração de código é LukeMapper.cs:GetDumbDeserializer() (linha 133)
LukeMapper repositório do GitHub
Toda ajuda é apreciada!Obrigado!
EDITAR:Então, eu simplifiquei o IL geração de código e é, essencialmente, o seguinte:
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>));
}
Eu adicionei no il.DeclareLocal(type)
(onde o tipo é PocoObject) por kvbs do comentário, mas eu não tenho certeza se eu estou colocando ele no lugar certo (ou se importa).
Solução
Ele é o sem sentido no final, e usando Stfld
para chamar uma propriedade;Eu não tenho nenhuma idéia de onde o efeito coisas vieram, mas eu não acreditar que veio de ExampleMethod
- Eu acho que pode ter sido a partir de uma versão anterior do seu.Em particular, você ainda não tiver definido um segundo local, para Ldloc_1
não faz sentido;mas não há absolutamente nenhuma necessidade para qualquer etiquetas / filiais aqui.Aqui está o que eu tenho, que funciona (nota: eu não sabia que o seu settableProperties
foram, então eu o fiz, usando apenas os 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>));
E para fins de comparação, aqui é o que eu recebo quando olhando para ExampleMethod
no reflector (em uma versão de compilação, 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
}
Observe o seguinte:
- sem rótulos / ramificação (eu não tenho nenhuma idéia de onde veio, mas que não é o que você postou)
- ele define um local
- nenhum
nop