منع DynamicMethod VerificationException العملية يمكن أن يزعزع استقرار وقت التشغيل
-
12-12-2019 - |
سؤال
أنا باستخدام IL جيل إلى إنشاء بسيط deserializer الأسلوب الذي يأخذ سلاسل من لوسين الوثيقة مجموعات أو خصائص الحقول إشارة من نوع object (بوكو).
كلما أحاول تشغيل المولدة طريقة استلام VerificationException خطأ.هناك أسئلة أخرى بخصوص هذا الخطأ عدد قليل من التي لها علاقة مع DynamicMethods ، ولكن من ما أستطيع أن أقول مسألة أواجه مختلفة.
العملية يمكن أن destablize-على-وقت التشغيل-و-dynamicmethod مع القيمة أنواع
msil-العملية-يمكن-زعزعة-على-وقت التشغيل-استثناء
على الرغم من أن الطريقة سوف تحصل على أكثر تعقيدا في المستقبل ، يجب فقط القيام سلسلة مهمة الآن.طريقة أنا أحاول خلق حيوي سوف تبدو تماما مثل هذا:
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;
}
حيث PocoObject يبدو مثل هذا:
class PocoObject
{
public string ID;
public string DisplayText;
public string PropId { get; set; }
}
حاولت تكرار IL الناتجة من تجميع رمز الوقت بالضبط (حتى بت لا لزوم لها) و يبدو مثل هذا:
.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'
لقد تمكنت من إنقاذ DynamicMethod إلى الجمعية على القرص ، تفتيشها و هذا هو بالضبط ما يجلب.خط عن الخط هو نفس وقت التحويل البرمجي طريقة IL.
ومع ذلك ، عند تنفيذ الأسلوب الديناميكي الخطأ أعلاه يتم طرح.لا أحد لديه أي فكرة كيف يمكنني إصلاح هذا ؟
ملاحظة:لدي شفرة المصدر على GitHub إذا كان أي شخص ترغب في إلقاء نظرة فاحصة.IL الجيل رمز في LukeMapper.cs:GetDumbDeserializer() (خط 133)
كل مساعدة تقدير!وذلك بفضل!
تحرير:لذلك يجب تبسيط IL جيل كود و هو أساسا ما يلي:
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>));
}
أضفت في il.DeclareLocal(type)
(حيث نوع PocoObject) في kvbs تعليق, ولكن لست متأكدا إذا أنا وضعه في المكان المناسب (أو إذا كان يهم).
المحلول
هو رطانة في النهاية ، واستخدام Stfld
استدعاء الممتلكات ؛ ليس لدي أي فكرة حيث أن نهاية الأشياء جاء ، لا نعتقد أنه جاء من ExampleMethod
- أعتقد أنه قد يكون من السابق بناء لك.وعلى وجه الخصوص ، لم يعرف الثاني ، Ldloc_1
لا معنى له;ولكن ليس هناك على الاطلاق لا حاجة لأي تسميات / فروع هنا.هنا هو ما يجب أن يعمل (ملاحظة لم أكن أعرف ما بك settableProperties
لذلك فعلت ذلك فقط باستخدام 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>));
ولأغراض المقارنة هنا هو ما تحصل عليه عندما تبحث في ExampleMethod
في عاكس (في بيان بناء ، الخ):
.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
}
الأشياء ملاحظة:
- لا التسميات / المتفرعة (ليس لدي أي فكرة من أين جاء هذا ، لكن ليس هذا هو ما نشرت)
- ويعرف المحلية
- لا
nop