evitar DynamicMethod VerificationException - operação poderia desestabilizar o tempo de execução

StackOverflow https://stackoverflow.com//questions/11680490

  •  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).

Foi útil?

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top