LoginSignup
1
2

More than 5 years have passed since last update.

直接 ILを書き下して作った実行ファイルをVisualStudioでステップ実行できるよう試みる。


以下のような疑似コードにブレークが当たるようにする。

code.text
Main
{
    smp = new Sample(12300, 45)
    sum = smp.GetSum()
    Console.WriteLine(sum.ToString())
    return
}

Define Sample
{
    field a : int
    field b : int

    Ctor(a: int, b: int)
    {
        this.a = a
        this.b = b
    }

    GetSum() : int
    {
        sum = this.a + this.b
        return sum
    }
}

実行ファイルを生成するプログラム。

Program.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Diagnostics.SymbolStore;
using System.IO;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            string exeName = "test.exe";
            AssemblyName asmName = new AssemblyName(exeName);
            AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Save);
            ModuleBuilder mb = ab.DefineDynamicModule(exeName, exeName, true);

            ISymbolWriter sw = mb.GetSymWriter();
            string codepath = Path.Combine(Directory.GetCurrentDirectory(), "code.txt");
            ISymbolDocumentWriter sdw = sw.DefineDocument(codepath, Guid.Empty, Guid.Empty, Guid.Empty);

            MethodBuilder main = BuildMain(sdw, mb);

            mb.CreateGlobalFunctions();
            ab.SetEntryPoint(main);
            ab.Save(exeName);

            return;
        }

        private static MethodBuilder BuildMain(ISymbolDocumentWriter sdw, ModuleBuilder mb)
        {
            SampleClassInfo cls = BuildSampleClass(sdw, mb);
            MethodBuilder main = mb.DefineGlobalMethod("Main", MethodAttributes.Static, typeof(void), Type.EmptyTypes);
            {
                ILGenerator il = main.GetILGenerator();

                // Sample smp;
                LocalBuilder locsmp = il.DeclareLocal(cls.Type);
                locsmp.SetLocalSymInfo("smp");

                // int sum;
                LocalBuilder locsum = il.DeclareLocal(typeof(int));
                locsum.SetLocalSymInfo("sum");
                il.Emit(OpCodes.Nop);

                // smp = new Sample(12300, 45);
                il.MarkSequencePoint(sdw, 4, 2, 4, 29); 
                il.Emit(OpCodes.Ldc_I4, 12300);
                il.Emit(OpCodes.Ldc_I4_S, (byte)45);
                il.Emit(OpCodes.Newobj, cls.Ctor);
                il.Emit(OpCodes.Stloc, locsmp);

                // sum = smp.GetSum();
                il.MarkSequencePoint(sdw, 5, 2, 5, 20);
                il.Emit(OpCodes.Ldloc, locsmp);
                il.EmitCall(OpCodes.Callvirt, cls.MethodGetSum, null);
                il.Emit(OpCodes.Stloc, locsum);

                // Console.WriteLine(sum.ToString());
                il.MarkSequencePoint(sdw, 6, 2, 6, 34);
                il.Emit(OpCodes.Ldloca_S, locsum);
                il.EmitCall(OpCodes.Call, typeof(int).GetMethod("ToString", Type.EmptyTypes), null);
                il.EmitCall(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), null);

                // return;
                il.MarkSequencePoint(sdw, 7, 2, 7, 8);
                il.Emit(OpCodes.Nop);
                il.Emit(OpCodes.Ret);
            }

            return main;
        }

        private class SampleClassInfo
        {
            public TypeBuilder Type { get; private set; }
            public ConstructorBuilder Ctor{ get; private set; }
            public MethodBuilder MethodGetSum { get; private set; }

            public SampleClassInfo(TypeBuilder type, ConstructorBuilder ctor, MethodBuilder methodGetSum)
            {
                this.Type = type;
                this.Ctor = ctor;
                this.MethodGetSum = methodGetSum;
            }
        }

        private static SampleClassInfo BuildSampleClass(ISymbolDocumentWriter sdw, ModuleBuilder mb)
        {
            // class Sample
            TypeBuilder tb = mb.DefineType(
                "Sample",
                TypeAttributes.AnsiClass | TypeAttributes.Class | TypeAttributes.NotPublic | TypeAttributes.BeforeFieldInit,
                typeof(object)
                );

            // private int a;
            FieldBuilder fa = tb.DefineField("a", typeof(int), FieldAttributes.Private);
            // private int b;
            FieldBuilder fb = tb.DefineField("b", typeof(int), FieldAttributes.Private);

            // constructor
            ConstructorBuilder ctor = tb.DefineConstructor(
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
                CallingConventions.HasThis | CallingConventions.Standard,
                new Type[] { typeof(int), typeof(int), }, null, null
                );
            {
                // argument a, b
                ParameterBuilder pa = ctor.DefineParameter(1, ParameterAttributes.In, "a");
                ParameterBuilder pb = ctor.DefineParameter(2, ParameterAttributes.In, "b");

                ILGenerator il = ctor.GetILGenerator();

                il.MarkSequencePoint(sdw, 16, 2, 16, 3);
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
                il.Emit(OpCodes.Nop);

                // this.a = a;
                il.MarkSequencePoint(sdw, 17, 3, 17, 13);
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldarg_1);
                il.Emit(OpCodes.Stfld, fa);

                // this.b = b;
                il.MarkSequencePoint(sdw, 18, 3, 18, 13);
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldarg_2);
                il.Emit(OpCodes.Stfld, fb);

                // return;
                il.MarkSequencePoint(sdw, 19, 2, 19, 3);
                il.Emit(OpCodes.Nop);
                il.Emit(OpCodes.Ret);
            }

            // int GetSum() {}
            MethodBuilder methodGetSum = tb.DefineMethod(
                "GetSum",
                MethodAttributes.Public | MethodAttributes.HideBySig,
                typeof(int), Type.EmptyTypes
                );
            {
                ILGenerator il = methodGetSum.GetILGenerator();

                // int sum;
                LocalBuilder locsum = il.DeclareLocal(typeof(int));
                locsum.SetLocalSymInfo("sum");

                il.MarkSequencePoint(sdw, 22, 2, 22, 3);
                il.Emit(OpCodes.Nop);

                // sum = this.a + this.b;
                il.MarkSequencePoint(sdw, 23, 3, 23, 24);
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldfld, fa);
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldfld, fb);
                il.Emit(OpCodes.Add);
                il.Emit(OpCodes.Stloc, locsum);

                // return sum;
                il.MarkSequencePoint(sdw, 24, 3, 24, 13);
                il.Emit(OpCodes.Ldloc, locsum);
                il.Emit(OpCodes.Ret);
            }

            tb.CreateType();
            return new SampleClassInfo(tb, ctor, methodGetSum);
        }
    }
}

ModuleBuilder.GetSymWriterISymbolWriter を取得。

ISymbolWriter.DefineDocument でソースコードファイルを指定して対応する ISymbolDocumentWriter を取得。

ILGenerator.MarkSequencePoint で、ILのコード位置がソースコードファイル上のどこに対応しているかを設定。
LocalBuilder.SetLocalSymInfo でローカル変数のシンボル名を定義。
ConstructorBuilderMethodBuilderDefineParameter でメソッド引数のシンボル名を定義。


このプログラムを実行すると test.exe と test.pdb が生成される。
同じディレクトリに code.txt を配置しておく。

VisualStudio を起動し、「ファイル」→「開く」→「プロジェクト・ソリューション」で test.exe を選択して開く。
code.txt をドロップして開く。
cap_a.png

code.txt にブレークポイントを設定(smp = new Sample() の行で)
F5 で実行すると、設定したブレークポイントで止まる。
cap_B.png

後は比較的普通にステップ実行できる。
cap_C.png

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2