2

重複の可能性:
Mono.Cecil はブランチなどの場所を処理しますか?

アプリで特定のメソッドが呼び出された後に静的メソッド呼び出しを挿入する必要があるプロジェクトに取り組んでいます。私は Mono Cecil を使用していますが、ほとんどの場合、すべて正常に動作します。ただし、更新されたアセンブリがディスクに書き込まれるときに、ブランチ オフセットが破損する場合がいくつかあります。私のコードは次のようになります。

public void InstrumentMethod(メソッド定義メソッド)
{
    var プロセッサ = method.Body.GetILProcessor();

    var instr = GetStaticInstrumentMethod();
    var instrument = method.Module.Import(instr);
    var call = method.Body.Instructions.First(i => i.IsCallImInterestedIn());
    processor.InsertBefore(call, instrument);
}

したがって、90% の場合、このコードは問題なく動作します。ターゲット メソッドは追加の呼び出しでインストルメント化され、すべて正常に動作します。

残りの 10% の時間は、インストルメント化されたメソッドを実行できず、ILSpy でそれを見ようとするとエラーが発生します。ILSpy エラーは次のとおりです。

ICSharpCode.Decompiler.DecompilerException: System.Object Savo.Utilities.Mapping.ReflectionMapper::MapTo(System.Object,System.Object) の逆コンパイル エラー
 ---> System.NullReferenceException: オブジェクト参照がオブジェクトのインスタンスに設定されていません。
   ICSharpCode.Decompiler.ILAst.ILAstBuilder.c__DisplayClass33.b__26 (ByteCode b) で
   System.Linq.Enumerable.Any[TSource] (IEnumerable`1 ソース、Func`2 述語) で
   ICSharpCode.Decompiler.ILAst.ILAstBuilder.ConvertLocalVariables (List`1 本体) で
   ICSharpCode.Decompiler.ILAst.ILAstBuilder.StackAnalysis (MethodDefinition methodDef) で
   ICSharpCode.Decompiler.Ast.AstMethodBodyBuilder.CreateMethodBody (IEnumerable`1 パラメーター) で
   ICSharpCode.Decompiler.Ast.AstMethodBodyBuilder.CreateMethodBody (MethodDefinition methodDef、DecompilerContext コンテキスト、IEnumerable`1 パラメーター) で
   --- 内部例外スタック トレースの終了 ---
   ICSharpCode.Decompiler.Ast.AstMethodBodyBuilder.CreateMethodBody (MethodDefinition methodDef、DecompilerContext コンテキスト、IEnumerable`1 パラメーター) で
   ICSharpCode.Decompiler.Ast.AstBuilder.CreateMethod (MethodDefinition methodDef) で
   ICSharpCode.ILSpy.CSharpLanguage.DecompileMethod (MethodDefinition メソッド、ITextOutput 出力、DecompilationOptions オプション) で
   ICSharpCode.ILSpy.TextView.DecompilerTextView.DecompileNodes (DecompilationContext コンテキスト、ITextOutput textOutput) で
   ICSharpCode.ILSpy.TextView.DecompilerTextView.c__DisplayClass10.b__f() で

ildasm を使用してコードを逆コンパイルしましたが、アセンブリの保存時にオフセットが更新される方法に問題があるように見えます。呼び出しのオフセットについては何もしていません (手動で設定しようとしましたが、同じ問題が発生しました)。私の問題のメソッドの 1 つのインストルメンテーション前の逆アセンブリは、次のようになります。

IL_000e:  br.s       IL_005e
IL_0010:  ldloca.s   CS$5$0000
IL_0012:  call       instance !0 valuetype         [mscorlib]System.Collections.Generic.List`1/Enumerator<class MyThing>::get_Current()
IL_0017:  stloc.0
IL_0018:  nop

... business logic ...    

IL_005d:  nop
IL_005e:  ldloca.s   CS$5$0000
IL_0060:  call       instance bool valuetype      [mscorlib]System.Collections.Generic.List`1/Enumerator<class MyThing>::MoveNext()
IL_0065:  stloc.3
IL_0066:  ldloc.3
IL_0067:  brtrue.s   IL_0010
IL_0069:  leave.s    IL_007a

インストルメント化された逆アセンブリを見ると、この例の IL_0067 に対応する行は次のようになります。

IL_00bc:    brtrue.s    IL_0129

IL_0129 が現在のメソッドに存在しないため、コンパイルできません。

私は気付きましたが、これが反復子の周りでのみ発生することをまだ証明していません。これまでに発生した 2 つのケースの C# コードは両方とも foreach ループであり、不適切な分岐ターゲットは両方とも foreach ループの終了条件に関連しているようです。

だから、私の質問は、オフセット参照を手動で管理する必要がありますか、それとも何か間違っていることがありますか?

4

0 に答える 0