2011-07-20 27 views
0

メソッドを生成するためにILの解析を試しています。私は各文字列がIL命令である文字列[]でメソッドのILコードを取得しました。私は再現する必要がILている。ここBr_S OpCodeを使用してReflection.Emit.Labelを使用して次の命令を指す

 foreach (string ins in instructions) //string representations of IL   
     { 
      string opCode = ins.Split(':').ElementAt(1); 

      // other conditions omitted 

      if (opCode.Contains("br.s")) 
      { 
       Label targetInstruction = ilGenerator.DefineLabel(); 

       ilGenerator.MarkLabel(targetInstruction); 

       ilGenerator.Emit(OpCodes.Br_S, targetInstruction); 
      } 

Source IL: 
IL_0000: nop 
IL_0001: ldstr "Hello, World!" 
IL_0006: stloc.0 
IL_0007: br.s IL_0009 
IL_0009: ldloc.0 
IL_000a: ret 

そして、ここでは、私は出力として取得しています何である私は、この配列をループしてILGeneratorを使用してオペコードを追加してい

Target IL: 
IL_0000: nop 
IL_0001: ldstr "Hello, World!" 
IL_0006: stloc.0 
IL_0007: br.s IL_0007 // this is wrong -- needs to point to IL_0009 
IL_0009: ldloc.0 
IL_000a: ret 

br.s呼び出しが無限ループを引き起こしていることを指摘しています。どのようにしてソースのように次の指示を指すようにするのですか?これはReflection.Emit.Labelの使用と関係がありますが、どのように動作するのか分かりません。 ILは、上で見ところで

EDITは、この単純な方法で、あなたがにをジャンプしたいオペコードを発する直前ilGenerator.MarkLabel()呼び出しを配置する必要があり

public string HelloWorld() 
    { 
      return "Hello, World!"; 
    } 
+0

私はここで見ていないよ何か...あなたは次の命令に分岐したくない理由がなければなりませんか?それは少し高価なノーオペレーションではありませんか?また、ブランチ命令をターゲットにしたくない場合は、ブランチ命令をラベルターゲットとしてマークするのはなぜですか? –

+0

Emit()呼び出しの後にMarkLabel()呼び出しを移動します。あるいはブランチを完全に省略すると、何もしません。 –

+0

@Seanあなたはあなたが頭の中にいることを間もなく分かっていると思います。完全なILアセンブラの実装を計画している場合は、分岐命令のために "string.Contains"よりもはるかに優れている必要があります。基本的にはラベルを作成し、それに適切な場所を見つける必要があります。すべてのブランチが「次の行のため」ではない。 –

答えて

4

このコード:

ilGenerator.MarkLabel(targetInstruction); 
ilGenerator.Emit(OpCodes.Br_S, targetInstruction); 

明確に「ここにラベルをマーク」とし、あなたはラベルをマーク時点で命令を追加しますと言います。

これがあなたが望むものでない場合、なぜそれをしていますか?

MarkLabelは、ラベルのターゲットとして出力する次の命令の位置を示す現在の位置をマークします。

この場合、「あなたが望むもの」を得るには、単にその2行を逆にして、ラベルをマーキングする前にブランチ命令を出力します。

私はその分岐命令のポイントを理解していないので、引用符で「あなたが望む」を配置しました。マシンは、次の命令だけを喜んで次の命令に「移動」させます。これを実行するには、「次の命令への分岐」命令を追加する必要はありません。

4

ためです。ブランチの前に配置しています。ブランチの前に配置すると、ブランチに分岐し、無限ループが作成されます。しかし、Lasseの言うとおり、ILを正しく放出すれば、それはノーオペレーションになるでしょう。

興味深いことに、全体の方法を容易とすることができる:

ldstr "Hello, World!" 
ret 

元のコードは、その著者LARTedを有する必要があるどのようなコンパイラ出射されます。

+0

私は参照してください。私はノーオペレーションが何であるか明確ではないのですか?とにかくILに単純なHelloWorld()メソッドの行が含まれているのはなぜですか? IL_0006からIL_0009にまっすぐに行くように思えます。 –

+0

No-op =操作なし。これは何も成し遂げないコードです。なぜILには地元や枝が含まれているのですか?いずれも必要ではない。 – cdhowie

+0

"nop"またはno-opは、 "no operation"を意味し、基本的にダミー命令、フィラー命令であり、何もしません。マシンはそれをスキップします。これは通常、位置合わせのために追加されますが、一部の最適化によってコードが再配置され、「穴」などが作成される可能性があります。 –

0

ILGeneratorでメソッドを呼び出すと、分岐点をマークし、次にEmit(OpCodes.Br_S, [label])をポイントに分岐することができます。

Hello WorldメソッドのIL命令をスパイするために使用したAPIは、デバッグモードで実行されたものとみなされます。追加されたnopおよび分岐命令が追加され、デバッガがすべてのステップをカバーするようになります。

DynamicMethodでは、デバッガをアタッチする必要はありません。また、プラットフォームによっては、リリースモードで追加の命令を実行することによってInvalidProgramExceptionが発生する可能性があります。

「Hello World」の方法のみ2命令が必要です(そして、それは非常に直感的です)

Ldstr "Hello, World!" 
Ret 
関連する問題