IL ループ処理をやってみる(はかせは小学生未満だったことが発覚してしまった)
あいさつ
どうも、はかせです。
とりあえずAssemblybuilderとDynamicMethodを使った
IL書きはなんとなくわかりました。
基本がわかったら次は応用です。
今回はループをやってみます。
ILでのループ
とりあえず下記のコードをILで起こすことを目指します。
int ArraySum(int[] numArray) { int sum = 0; foreach(var n in numArray) { sum += n; } return sum; }
まずはゴールを知るため上のコードを
LINQPadでILに変換してみます。
(これはメソッド単品で打ち込んだからstaticメソッドのIL
このldarg.0の部分をldarg.1にしなきゃいけない)
g__ArraySum|0_1: IL_0000: ldc.i4.0 IL_0001: stloc.0 // sum IL_0002: ldc.i4.0 IL_0003: stloc.1 // i IL_0004: br.s IL_0010 IL_0006: ldloc.0 // sum IL_0007: ldarg.0 IL_0008: ldloc.1 // i IL_0009: ldelem.i4 IL_000A: add IL_000B: stloc.0 // sum IL_000C: ldloc.1 // i IL_000D: ldc.i4.1 IL_000E: add IL_000F: stloc.1 // i IL_0010: ldloc.1 // i IL_0011: ldarg.0 IL_0012: ldlen IL_0013: conv.i4 IL_0014: blt.s IL_0006 IL_0016: ldloc.0 // sum IL_0017: ret
そしてこれを実際にDLLで吐くコード
static void CreateDLL() { const string ModuleName = "Hogehoge"; //.NET 4.5から。それ以前ではAppDomain.CurrentDomain.DefineDynamicAssemblyをかわりに使う var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(ModuleName), AssemblyBuilderAccess.RunAndSave); //DefineDynamicAssembly→DefineDynamicModuleの流れはお決まりの流れらしい var moduleBuilder = assemblyBuilder.DefineDynamicModule(ModuleName, ModuleName + ".dll"); //Hoge型を定義 var typeBuilder = moduleBuilder.DefineType("Hoge", TypeAttributes.Public); //Hoge型からSumインスタンスメソッドを定義 //第一引数にメソッド名を、第二引数にはアクセス修飾子、第三引数は戻り値、第四引数はメソッドの引数 var sum = typeBuilder.DefineMethod("Sum", MethodAttributes.Public, typeof(int), new Type[] { typeof(int[]) }); // IL手書きはいつもどおり var il = sum.GetILGenerator(); //ジャンプ先のラベルを宣言 var startLabel = il.DefineLabel(); var endLabel = il.DefineLabel(); var iLoacl = il.DeclareLocal(typeof(int)); //ローカル変数宣言 var iLoacl = il.DeclareLocal(typeof(int)); var iLoacl2 = il.DeclareLocal(typeof(int)); //インスタンスメソッドじゃないからarg0から引数←何を言ってるんだ馬鹿が //これは紛れもなくインスタンスメソッドだよ!!! il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Stloc_1); il.Emit(OpCodes.Br_S, startLabel); il.MarkLabel(endLabel); il.Emit(OpCodes.Ldloc_0); //il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldelem_I4); il.Emit(OpCodes.Add); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Add); il.Emit(OpCodes.Stloc_1); il.MarkLabel(startLabel); il.Emit(OpCodes.Ldloc_1); //il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldlen); il.Emit(OpCodes.Conv_I4); il.Emit(OpCodes.Blt_S, endLabel); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ret); //CreateTypeで型を作る var hogeType = typeBuilder.CreateType(); //クラスインスタンスを生成する var instance = Activator.CreateInstance(hogeType); var array = new int[] { 1, 2, 3 }; try { //さっき取得したインスタンスのSumメソッドを呼び出し、結果を格納 var result = hogeType.GetMethod("Sum").Invoke(instance, new object[] { array }); Console.WriteLine(result); } catch(System.Exception e) { Console.WriteLine(e.InnerException); } // 保存する時はDefineDynamicModuleの時に指定したのと同じ名前で吐くのが安全のために良い #if DEBUG try { assemblyBuilder.Save(ModuleName + ".dll"); Console.WriteLine("DllSaveSuccuss"); } catch (Exception e) { Console.WriteLine("SaveFaild" + e); } #endif }
こんな感じのコード書いて実行したらdll吐けたんですが、
結果が常に0でした。
※追記 2019/09/17
インスタンスメソッドとstaticメソッドの作り方をごちゃるっていう
初歩的なミスでしたm(__)m
あとがき
今回はILのループ処理をやってます。(現在進行形でバグフィックスなう)
色々わからないのでトライアンドエラーでやっていきます。
※追記 2019/09/17
解決しました。
今回はstaticメソッドとインスタンスメソッドの作り方を間違うという
小学生でもしないような大ポカ決めてしまいました。
ただやっぱりIL書きってコード保管もなんも起きないんで
こういう初歩的なミスしやすいんですかね。
そして参考に使ったLINQPadには最小限のことしか書かなかったせいで
static版のILを吐いていたというね・・・
めんどくさがらずちゃんとLINQPadにコード打ち込むようにします。
それでは今回はこの辺でノシ(´;ω;`)