Prevenire la DynamicMethod VerificationException: il funzionamento potrebbe destabilizzare il runtime
-
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
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).
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