none
ILGenerator.MarkLabelの位置について RRS feed

  • 質問

  • 下記プログラムはなぜか動作しません。その理由が知りたくて書き込みました。gen.MarkLabelの位置が悪いのかOpCodes.Call後に移動させると"動いた!"という文字が表示されます。または、Opcodes.Ldc_I4からOpCodes.Brtureまでをコメントにすると動きます。

    public delegate void ScriptRunner();
      
      static void Main(string[] args)
      {
       DynamicMethod dm = new DynamicMethod(
         "", null, null, typeof(StartupClass).Module);
       ILGenerator gen = dm.GetILGenerator();
       Label ET1 = gen.DefineLabel();

       ////////////////////////////////////////////////////////
       gen.Emit(OpCodes.Ldc_I4, 0);
       gen.Emit(OpCodes.Ldc_I4, 1);
       gen.Emit(OpCodes.Ceq );// 0!=1なので0がpush
       gen.Emit(OpCodes.Brtrue, ET1); // falseなので、ET1へは飛ばない
       
       gen.Emit(OpCodes.Ldstr, "動いた!");

       gen.MarkLabel(ET1); // これをコメントして下に移動させると動く
       
       MethodInfo mi = typeof(Console).GetMethod("Write", new Type[]{ typeof(string)});
       gen.Emit(OpCodes.Call, mi);

       //gen.MarkLabel(ET1);

       gen.Emit(OpCodes.Ret);
       /////////////////////////////////////////////////////////

       ScriptRunner run = (ScriptRunner)dm.CreateDelegate(typeof(ScriptRunner));
       run();
      }         
       

    2006年10月14日 8:11

回答

  • 今更ですが.

     

     hotcake さんからの引用

    下記プログラムはなぜか動作しません。その理由が知りたくて書き込みました。gen.MarkLabelの位置が悪いのかOpCodes.Call後に移動させると"動いた!"という文字が表示されます。または、Opcodes.Ldc_I4からOpCodes.Brtureまでをコメントにすると動きます。

     

    CIL では,処理フローが分岐した場合でも各 IL の位置で評価スタックのオブジェクトと型が同じになることを要請しています.

    上記プログラムは ET1 の位置のスタック状態が一意に定まらないのでエラーになります.『JIS X 3016』の「1.8.1 正当性検証可能なCIL のためのフロー制御の制限」などを参照してください.

     

    Verifier は IL の検証時に実際の動作の模倣は行わないので,OpCodes.Ceq の結果分岐が起きた場合と起きない場合の両方のフローを検証します.

    その結果,「これをコメントして下に移動させると動く」という場所の評価スタックの内容は

    1. 分岐が起きなかった場合,string オブジェクトがひとつ評価スタックに格納されている
    2. 分岐が起きた場合,評価スタックは空である

    ということになり,検証に失敗します.

    2007年10月3日 7:43