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); } }