evitar DynamicMethod VerificationException - operación podría desestabilizar el tiempo de ejecución

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

  •  12-12-2019
  •  | 
  •  

Pregunta

Estoy usando IL generación para crear un simple deserializer método que lleva cuerdas de un Lucene documento y establece las propiedades o campos de una referencia de tipo objeto (POCO).

Cada vez que intento ejecutar el método generado, voy a recibir un VerificationException de error.Hay otras preguntas acerca de este error, algunos de los cuales tienen que ver con DynamicMethods, pero puedo decir que el problema que estoy teniendo es diferente.

operación-podría-destablize-el-tiempo de ejecución-y-dynamicmethod-con-valor-de los tipos

msil-operación-que podría desestabilizar el tiempo de ejecución de excepción

Aunque el método será más complicado en el futuro, tengo que hacer simplemente de la cadena de asignación de ahora.El método que yo estoy tratando de crear de forma dinámica se verá exactamente como esta:

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

Donde PocoObject se parece a esto:

class PocoObject
{
    public string ID;
    public string DisplayText;

    public string PropId { get; set; }

}

He tratado de reproducir la IL generado a partir de la compilación de código de tiempo EXACTAMENTE (incluso la innecesaria bits) y se parece a esto:

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

He conseguido ahorrar el DynamicMethod a una asamblea en el disco, inspeccionado, y esto es exactamente lo que trae.La línea es el mismo que el tiempo de compilación método IL.

Sin embargo, cuando se ejecuta el método dinámico, el error anterior se produce.¿Alguien tiene alguna idea de cómo podría solucionar este problema?

Nota:Tengo el código fuente en GitHub si alguien quiere echar un vistazo más de cerca.La IL Generación de código es en LukeMapper.cs:GetDumbDeserializer() (línea 133)

LukeMapper repo de GitHub

Ayuda es muy apreciada.Gracias!


EDITAR:Así que he simplificado la IL generación de código y es, esencialmente, las siguientes:

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

He añadido en el il.DeclareLocal(type) (donde tipo es PocoObject) por kvbs del comentario, pero no estoy seguro de si lo estoy poniendo en el lugar correcto (o si lo que importa).

¿Fue útil?

Solución

Es el galimatías al final, y el uso de Stfld para llamar a una propiedad;No tengo idea de donde ese fin de cosas, pero yo no creo que vino de ExampleMethod - Creo que podría haber sido de una generación anterior de los suyos.En particular, no se ha definido un segundo local, por lo que Ldloc_1 no tiene ningún sentido;pero no hay absolutamente ninguna necesidad de etiquetas / sucursales aquí.Aquí es lo que tengo, que funciona (nota yo no sabía lo que su settableProperties fueron, así que lo hice solo uso 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>));

Y para efectos de comparación, aquí es lo que me pasa cuando se mira en ExampleMethod en el reflector (en una versión de lanzamiento, 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 
}

Cosas a tener en cuenta:

  • no hay etiquetas / de ramificación (no tengo idea de dónde vino, pero que no es de lo que has publicado)
  • se define un local
  • no nop
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top