はかせのラボ

私の頭の中を書いていく雑記ブログです

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にコード打ち込むようにします。

それでは今回はこの辺でノシ(´;ω;`)