Prevenire la DynamicMethod VerificationException: il funzionamento potrebbe destabilizzare il runtime

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

  •  12-12-2019
  •  | 
  •  

Domanda

Sto usando Il Generation per creare un semplice metodo di deserializzatore che prende stringhe da un documento di Lucene e imposta proprietà o campi di un oggetto tipo di riferimento (Poco).

Ogni volta che provo a eseguire il metodo generato, ricevo un errore di verificaException. Ci sono altre domande riguardanti questo errore, alcuni dei quali hanno a che fare con dinamiciMetodi, ma da quello che posso dire al problema che sto avendo è diverso.

Operation-Bister-desablize-the-runtime -e-Dynamicmethod-with-Value-Types

MSIL-operation-potrebbe-destabilize-the-runtime-exception

Sebbene il metodo diventerà più complicato in futuro, lo faccio solo facendo un incarico di corde in questo momento. Il metodo che sto cercando di creare dinamicamente sembrerà esattamente questo:

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

Dove PocoObject è simile a questo:

class PocoObject
{
    public string ID;
    public string DisplayText;

    public string PropId { get; set; }

}
.

Ho provato a replicare il Generato dal codice di compilazione esattamente (anche i bit non necessari) e sembra questo:

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

Sono riuscito a salvare il dinamicoMetEthod su un assembly sul disco, l'ha ispezionato, e questo è esattamente ciò che porta. Linea per la linea è la stessa del metodo di compilazione il.

Tuttavia, quando si esegue il metodo dinamico, viene generato l'errore sopra. Qualcuno ha idea di come potrei risolvere questo?

Nota: ho il codice sorgente su GitHub se qualcuno vorrebbe fare uno sguardo più da vicino. Il codice Generation è in lukemapper.cs: GetDombDeserializer () (linea 133)

lukemapper github repo

Aiuto è apprezzato! Grazie!


.

Modifica: quindi ho semplificato il codice di generazione IL e è essenzialmente quanto segue:

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

Ho aggiunto nel il.DeclareLocal(type) (dove il tipo è PocoObject) per il commento di KVBS, ma non sono sicuro se lo sto mettendo nel posto giusto (o se non importa).

È stato utile?

Soluzione

è il gibberish alla fine e usando Stfld per chiamare una proprietà;Non ho idea di dove venisse quelle cose finali, ma io non credi che venisse da ExampleMethod - penso che potrebbe essere stato da una precedente build.In particolare, non hai definito un secondo locale, quindi Ldloc_1 non ha senso;Ma non c'è assolutamente alcun bisogno di etichette / rami qui.Ecco cosa ho, che funziona (nota non sapevo cosa fossero il tuo settableProperties, quindi l'ho fatto solo usare 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 per scopi di confronto, ecco cosa ottengo quando guardo ExampleMethod in riflettore (in una creazione di rilascio, ecc.):

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

Cose da notare:

    .
  • Nessuna etichetta / ramificazione (non ho idea di dove è venuto da quello, ma non è da quello che hai postato)
  • Definisce un locale
  • No nop
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top