LCGと動的メソッド

Joel Pobar's weblogより
hello, world... LCG (Lightweight Code Gen) style!
あまり話題になってませんが.NET2.0の新機能にLCG(Lightweight Code Gen)というのがあります。.NET1.1ですと動的にメソッドを作るにはアセンブリとモジュール(必要に応じてタイプも)を生成することになり、オーバーヘッドが大きかったのですが、.NET2.0では静的メソッドならオーバーヘッド無しで作成出来るようになるようです。動的言語を作る場合に役立ちそうですね。

ところで、LCG以前にメソッドの生成自体やったこと無かったので、勉強がてらReflection.Emitをちょっと弄ってみました。まともにILアセンブラ覚えてないのでフィボナッチ関数程度でも嵌る嵌る。(^^;

using System;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;

// 動的メソッドクラス(グローバル専用)
public class DynamicMethod : IDisposable {
  private string name;
  private ModuleBuilder module;
  private MethodBuilder method;
  private string tmpFileName;
  private bool created = false;
  private bool disposed = false;

  public DynamicMethod(string name, Type rType, Type pTypes) {
    this.name = name;
    // モジュール名にテンポラリファイル名を利用
    tmpFileName = Path.GetTempFileName();
    AssemblyName asmName = new AssemblyName();
    asmName.Name = Path.GetFileNameWithoutExtension(tmpFileName);
    AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(
      asmName, AssemblyBuilderAccess.Run);
    module = ab.DefineDynamicModule(tmpFileName);
    method = module.DefineGlobalMethod(name, 
      MethodAttributes.Static | MethodAttributes.Public, rType, pTypes);
  }

  ~DynamicMethod() {
    Dispose(false);
    GC.SuppressFinalize(this);
  }

  public ILGenerator GetIlGenerator() {
    return method.GetILGenerator();
  }

  public object Invoke(object obj, params object ps) {
    if (!created) {
      // コード生成
      module.CreateGlobalFunctions();
      created = true;
    }
    return module.GetMethod(name).Invoke(obj, ps);
  }

  public void Dispose() {
    Dispose(true);
  }

  protected void Dispose(bool flag) {
    if (flag) {
      module = null;
      method = null;
    }
    if (!disposed) {
      // テンポラリファイルを削除する
      File.Delete(tmpFileName);
      disposed = true;
    }
  }
}

public class MyApp {
  // この関数を動的に作成する
#if false
  public static void Fib(int n) {
    int a=0, b=1, t;
    for (int i=0; i<n; ++i) {
      Console.WriteLine(b);
      t=a;
      a=b;
      b=t+b;
    }
  }
#endif
  public static void Generate(ILGenerator il) {
    // ラベル
    Label LOOP = il.DefineLabel();
    Label END = il.DefineLabel();

    // ローカル変数
    // int a, b, t, i;
    il.DeclareLocal(typeof(int));  // a=0
    il.DeclareLocal(typeof(int));  // b=0
    il.DeclareLocal(typeof(int));  // t=0
    il.DeclareLocal(typeof(int));  // i=0  

    // b = 1;
    il.Emit(OpCodes.Ldc_I4_1);
    il.Emit(OpCodes.Stloc_1);

    // ループラベル
    il.MarkLabel(LOOP);

    // i < n (引数)
    il.Emit(OpCodes.Ldloc_3); il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Bge, END);

    // Console.WriteLine(b);
    il.Emit(OpCodes.Ldloc_1);
    il.Emit(OpCodes.Call, 
      typeof(Console).GetMethod("WriteLine",new Type{typeof(int)}));

    // t = a;
    il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Stloc_2);

    // a = b;
    il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Stloc_0);

    // b = t + b;
    il.Emit(OpCodes.Ldloc_2); il.Emit(OpCodes.Ldloc_1); 
    il.Emit(OpCodes.Add);     il.Emit(OpCodes.Stloc_1);

    // ++i;
    il.Emit(OpCodes.Ldloc_3); il.Emit(OpCodes.Ldc_I4_1);
    il.Emit(OpCodes.Add);     il.Emit(OpCodes.Stloc_3);
    il.Emit(OpCodes.Br, LOOP);

    // ループエンド
    il.MarkLabel(END);  

    // リターン
    il.Emit(OpCodes.Ret);
  }

  public static void Main() {
    // Fib関数を動的に作成する
    DynamicMethod fib = new DynamicMethod("Fib",typeof(void),new Type {typeof(int)});
    // Fib関数のコード生成
    Generate(fib.GetIlGenerator());
    // 関数呼び出し
    fib.Invoke(null, 20);
  }
}