DynamicMethod VerificationException 방지 - 작업으로 인해 런타임이 불안정해질 수 있음

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

  •  12-12-2019
  •  | 
  •  

문제

IL 생성을 사용하여 Lucene 문서에서 문자열을 가져오고 참조 유형 개체(POCO)의 속성이나 필드를 설정하는 간단한 역직렬 변환기 메서드를 만들고 있습니다.

생성된 메서드를 실행하려고 할 때마다 VerificationException 오류가 발생합니다.이 오류에 관한 다른 질문이 있으며 그 중 일부는 DynamicMethods와 관련이 있지만 내가 겪고 있는 문제를 알 수 있는 내용은 다릅니다.

작업이 런타임을 불안정하게 만들 수 있으며 값 유형이 있는 동적 메서드

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 GitHub 레포

모든 도움에 감사드립니다!감사해요!


편집하다:그래서 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