منع DynamicMethod VerificationException العملية يمكن أن يزعزع استقرار وقت التشغيل

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

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

LukeMapper جيثب الريبو

كل مساعدة تقدير!وذلك بفضل!


تحرير:لذلك يجب تبسيط 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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top