2004-08-25 (Wed) [長年日記]
_ tdiarytimes-flashstyle.rb
日記を登録した時間帯をタイムライン上に記録します。記録されたエントリは日時の経過と共にフェードアウトしていきます。
[trd by phonondrive - tdiarytimes-flashstyle.rbより引用]
今更ながら使ってみた。
_ monoのgenericsとrelfection API
monoでrelfection APIを使って、
class Foo<T> { public static void Bar(T o) { System.Console.WriteLine(o); } } class Test { static void Main () { Foo<int>.Bar(0); } }
のようなコードを生成してみた。
using System; using System.Reflection; using System.Reflection.Emit; public class GenericTypeCreator { public static void Main() { AppDomain domain = AppDomain.CurrentDomain; AssemblyName assemblyName = new AssemblyName(); assemblyName.Name = "test"; AssemblyBuilder assembly = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave); ModuleBuilder module = assembly.DefineDynamicModule("test", "test.exe"); TypeBuilder foo = module.DefineType("Foo", TypeAttributes.Public | TypeAttributes.Class, typeof(object)); GenericTypeParameterBuilder[] typeParameters = foo.DefineGenericParameters(new string[] { "T" }); GenericTypeParameterBuilder typeParameter = typeParameters[0]; MethodBuilder bar = foo.DefineMethod("Bar", MethodAttributes.Public | MethodAttributes.Static, null, new Type[] { typeParameter }); ILGenerator barIl = bar.GetILGenerator(); MethodInfo writeLine = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(object) }); barIl.Emit(OpCodes.Ldarg_0); barIl.Emit(OpCodes.Box, typeParameter); barIl.EmitCall(OpCodes.Call, writeLine, null); barIl.Emit(OpCodes.Ret); MethodBuilder main = foo.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static, null, Type.EmptyTypes); ILGenerator mainIl = main.GetILGenerator(); Type gt = foo.GetGenericTypeDefinition(); Type intFoo = gt.BindGenericParameters(new Type[] { typeof(int) }); MethodInfo intBar = null; foreach (MethodInfo m in intFoo.GetMethods()) { if (m.Name == "Bar") intBar = m; } mainIl.Emit(OpCodes.Ldc_I4_0); mainIl.EmitCall(OpCodes.Call, intBar, null); mainIl.Emit(OpCodes.Ret); foo.CreateType(); assembly.SetEntryPoint(main, PEFileKinds.ConsoleApplication); assembly.Save("test.exe"); } }
実行結果:
excelsior:gmcstest$ gmcs codegen.cs && ./codegen.exe && ./test.exe ALPHA SOFTWARE: Mono C# Compiler 0.96.0.0 for Generics Compilation succeeded 0
とこう書いてしまうと、簡単なのだが、ちょっとしたことではまって、えらく 時間がかかってしまった。
Type#BindGenericParameters()は、Type#GetGenericTypeDefinition()で 取得したTypeオブジェクトに対して呼び出さなければならない。 ところが、TypeBuilderに対して呼び出していたため、NullReferenceExceptionが 発生していた。 *1
やっぱりエラーメッセージ重要。 *2
_ monoのJITコンパイラが生成したx86コードを読む
monoに-v -v -vというオプションを与えるとディスアセンブルして表示して くれるようだ。
you can disassenble the code
do this:
mono -v -v -v foo.exe > x
[Exploring: What does newobj look like?より引用]
ちなみにILレベルのディスアセンブラはmonodisという別のコマンド。 こちらの方にはよくお世話になっている。
_ babel
おっ。Satherの.NET実装だ。ちょっと期待しちゃうぞー。IteratorとParametrized classを、CLI(.NET2.0)のIteratorとGenericsにいかに統合するかが鍵かな。 あとはCode inclusionがネックか。
[oka326 blogより引用]
おお、はじめてblogで反応が。
しかし、ずっとほったらかしてたので期待させてしまって申し訳ない気もする。 Satherと互換性なくなるかもしれないし。 *1
一番悩ましいのはcode inclusionですね。C#の普通の継承とどう辻褄を 合わせるか。
相互運用性を確保するには何らかの形でC#的な継承を許さないといけない と思うけど、SatherにそのままC#的な継承を追加してしまうのもどうかと。 今のところ、C#のクラスから継承したクラスの子孫のみに普通の継承を許して、それらはSatherのクラスにincludeすることはできないようにする、 くらいしか思いつかない。 あと、ソースがないクラスもincludeできた方がうれしいとは思うけど、 今のところプライオリティは低いですね。
当面の目標はparameterized typeのサポート。
しかし、コンストラクタの問題とか、色々悩みはつきないなあ。
*1 もっともSatherとの互換性なんてあんまり重要じゃなさそうだけど。
どうも、blog見つけていただいたようです。Satherで書いているかと思いきやC#だったんで意外といえば意外。でもその方がいいですね。Code inclusionなんぞいらんので、Satherを断ち切ってどんどんいい言語に仕上げて言ってください。.NETとの親和性の高さに期待してます:-)