投稿者 魔界の仮面弁士  (社会人) 投稿日時 2010/5/17 21:37:35
> 関係ないかも知れませんが、
無関係とは言いませんが、直接の関連性は見出せませんでした。

葉月さんのコードが行っていることは、GetILAsByteArray で IL を読み取り、それを DynamicMethod 経由で
呼び出すという行為ですよね。これはどちらかというと、動的にメソッドを定義/実行するときに
使われる物であって、元のコードを出力したいという目的からは外れているように思えます。
http://d.hatena.ne.jp/akiramei/20040722/p1


もしも実行だけが目的なら、IL のバイナリを得る必要は無く、もっと単純に書くことができます。
既にデリゲートインスタンスは取得されているとのことでしたから、たとえばこんな感じ。
Dim fn As Func(Of IntegerIntegerInteger) = AddressOf CHoge.MyAdd
Dim ret As Integer = fn(iA, iB)
または
Dim mi As MethodInfo = GetType(CHoge).GetMethod("MyAdd", BindingFlags.Static Or BindingFlags.Public)
ret = CInt(mi.Invoke(NothingNew Object() {iA, iB}))


一方、IL のバイナリを VB のソースコードに変換するという視点から考えてみた場合、方針としては
 1. IL のバイナリが、どの System.Reflection.Emit.OpCodes に相当するかを調べる。
 2. それぞれの OpCode を解釈して、CodeDOM へと展開する。
 3. そこから GenerateCodeFromMember メソッドでソースコードへと展開。
という手順を思いついたのですが、2. が鬼門なので、あまり現実的な解では無さそうです。
(2 のコードが書ける人なら、そもそも IL 自体を読めるはずですし)


>> ILのコードを呼び出すのだと思うのですが、
>> 読んで理解するのは難しそうですので、諦めようと思います。。
たとえば葉月さんの CHoge.MyAdd を例に挙げると、GetILAsByteArray からは
"00-02-03-D6-0A-2B-00-06-2A" の 9 バイトのバイナリが返されていました。

これを System.Reflection.Emit.OpCodes に照らし合わせると、下記のような意味になります。
分かりにくいですが、これが「Return iA + iB」の内部処理というわけです。

00 : OpCodes.Nop      (nop : 何もしない)
02 : OpCodes.Ldarg_0  (ldarg.0 : 引数#0を stack にロード)
03 : OpCodes.Ldarg_1  (ldarg.1 : 引数#1を stack にロード)
D6 : OpCodes.Add_Ovf  (add.ovf : stack から値を2つ取り出し、加算結果をstack に入れる)
0A : OpCodes.Stloc_0  (stloc.0 : stack から値を1つ取り出し、インデックス#0の変数に移動)
2B : OpCodes.Br_S     (br.s : <int8> で指定されたオフセットの位置に無条件ジャンプする)
 00 : オフセット 0x00
06 : OpCodes.Ldloc_0  (ldloc.0 : インデックス#0の変数を stack にロード)
2A : OpCodes.Ret      (ret : stack から値を1つ取り出し、それを戻り値としてメソッドから戻る)
(Add_Ovf では、加算結果のオーバーフローチェックも行われます)


良く見ると、この中には無意味な処理が幾つかありますので、それらを整理すると
"02-03-D6-2A" の 4 バイトだけでも済ませられそうです。
Dim dm As New DynamicMethod( _
    "HogetyauYo!", _
    GetType(Integer), _
    New Type() {GetType(Integer), GetType(Integer)}, _
    GetType(CHoge), _
    False)

Dim info As DynamicILInfo = dm.GetDynamicILInfo()
info.SetCode(New Byte() {&H2, &H3, &HD6, &H2A}, 2)
info.SetLocalSignature(New Byte() {&H7, 0})

Dim iA As Integer = 5
Dim iB As Integer = 2
Dim ret As Integer = CInt(dm.Invoke(NothingNew Object() {iA, iB}))



おまけ。この"02-03-D6-2A" の処理を ILGenerator 経由で生成してみました。
デリゲートインスタンス内のコードを出力するという目的からは外れますが。
Dim dm As New DynamicMethod( _
    "MyAdd", _
    GetType(Integer), _
    New Type() {GetType(Integer), GetType(Integer)})

Dim g As ILGenerator = dm.GetILGenerator()
g.Emit(OpCodes.Ldarg_0)          '02: 引数#0の読み込み 
g.Emit(OpCodes.Ldarg_1)          '03: 引数#1の読み込み 
g.Emit(OpCodes.Add_Ovf)          'D6: 加算 
g.Emit(OpCodes.Ret)              '2A: Return 

Dim iA As Integer = 5
Dim iB As Integer = 2
Dim ret As Integer = CInt(dm.Invoke(NothingNew Object() {iA, iB}))


元の "00-02-03-D6-0A-2B-00-06-2A" のコードだと、こんな感じ。
Dim g As ILGenerator = dm.GetILGenerator()
g.DeclareLocal(GetType(Integer)) 'ローカル変数。強いて言えば「Dim MyAdd As Integer」に相当。 
g.Emit(OpCodes.Nop)              '00: 何もしない 
g.Emit(OpCodes.Ldarg_0)          '02: 引数#0の読み込み 
g.Emit(OpCodes.Ldarg_1)          '03: 引数#1の読み込み 
g.Emit(OpCodes.Add_Ovf)          'D6: 加算 
g.Emit(OpCodes.Stloc_0)          '0A: 変数#0に代入 
g.Emit(OpCodes.Br_S, CByte(0))   '2B,00: 次の行へジャンプ 
g.Emit(OpCodes.Ldloc_0)          '06: 変数#1を読み込み 
g.Emit(OpCodes.Ret)              '2A: Return