Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

動的なILの生成と編集

7,302 views

Published on

.NET基礎勉強会(http://connpass.com/event/2441/)での発表資料のバグ修正版です

Published in: Technology
  • Was a little hesitant about using ⇒⇒⇒WRITE-MY-PAPER.net ⇐⇐⇐ at first, but am very happy that I did. The writer was able to write my paper by the deadline and it was very well written. So guys don’t hesitate to use it.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

動的なILの生成と編集

  1. 1. 動的なILの生成/編集 terurou 2013/07/20
  2. 2. 目次 • 自己紹介 • IL生成の予備知識 • ILの生成方法 • Windows Store AppsでのIL生成 • まとめ
  3. 3. 自己紹介
  4. 4. 7月から無職してます。
  5. 5. 技術領域 • Haxe/JavaScriptとかWindows Store Appsとか WPFとかSilverlightとかできます。 • Haxe/JavaScriptは「なごやまつり」で話す予定です。 • ギョーム系DataGridの実装に定評があります。 • たぶんシステムアーキテクチャ設計が本業です。 • 最近はF#でWebSocketサーバを試作してました。
  6. 6. コミュニティ活動 - DSTokaiとNGK • DSTokai : 東海地方のメタコミュニティ • IT系イベントカレンダーとコミュニティ連絡用ML • http://go.dstokai.info/ • NGK : なごや ごうどう こんしんかい • 毎年忘年会を開催しています • 一昨年は100人、去年は120人 • ちなみに今年は12/7に開催予定 • 花見はここ数年サボってます
  7. 7. ILと私 • IronPythonとSilverlight2でR&D(3年前) • Silverlight上でScalaアプリを実行(2年前) • Silverlightアプリを動的生成(1年半前) • Store AppsからDesktop用EXE生成(1年前) • その他、.NETのお仕事で仕方なく…
  8. 8. IL生成の予備知識 photo: http://www.flickr.com/photos/seditiouscanary/1279041211/, CC BY-NC-ND 2.0
  9. 9. ILとは • .NETのアセンブリ言語 • IL = Intermediate Language = 中間言語 .assembly extern mscorlib { .publickeytoken = (b7 7a 5c 56 19 34 e0 89) .ver 4:0:0:0 } .assembly Test { .hash algorithm 0x00008004 .ver 1:0:0:0 } .module Test.exe .class auto ansi Test.Program extends [mscorlib]System.Object { .method static void Main (string[] args) cil managed { .maxstack 8 .entrypoint IL_0000: ldstr "Hello World" IL_0005: call void [mscorlib]System.Console::WriteLine(string) IL_000a: ret } }
  10. 10. .NETとILの関係図 Compiler ilasm ildasm CLR icons: http://www.gentleface.com/, CC BY-NC 3.0 C#, VB, F#, ... IL Assembly Native 実行 Assembly(PE:Portable Executable) .NETの実行バイナリ(EXE, DLL) ilasm ILアセンブラ .NET Frameworkに標準付属 ildasm IL逆アセンブラ .NET Frameworkに標準付属
  11. 11. 動的なILの生成 .NETにはILを動的(アプリ実行時)に生成するAPIが 標準で備わっています。 • 静的な型やメソッドの作成 • 生成したILの即時実行 • Assembly(EXE, DLL)の生成・保存 • 既存APIの編集(Monkey Patching)は不可能 • 別の手段を使えば編集することも可能
  12. 12. IL生成の利用例 • 処理の高速化 • 動的メソッドコール(Reflection) • シリアライザ(動的に静的型を生成) • 透過プロキシやMockの内部実装 • アスペクト指向/Weaving • 自作コンパイラのバックエンド
  13. 13. 本当にIL生成が必要か? • IL生成は黒魔術 • 利用例で紹介した通り、必要な個所は限定される • 問題点も多い • 低レベルAPI • 可読性やメンテナンス性の低下 • プログラミング言語の型制約の無視できる • 型安全ではなくなり、品質保証にコストがかかる
  14. 14. IL生成の前に検討すべきもの • デザインパターンやIoC Container • コードの書き方で逃げられないか? • T4テンプレート(自動コード生成) • コンパイル前にどうにかならないか? • System.Dynamic(C# 4.0のdynamic型) • 動的プロパティや動的メソッドコール等で利用 • 内部的には実行時に自動でILを生成している • 下手に手書きでILを生成するよりも高速 • F#(高度な型システム, TypeProvider) • C#やVBの型システムが貧弱だからなのでは??
  15. 15. ILの生成方法
  16. 16. IL生成の基本的な手順 1. C#でひな形コードを書いてビルド 2. Assemblyを逆アセンブリ • ildasmやILSpy等を利用する 3. 逆アセンブリした結果を見ながらコードを書く
  17. 17. 逆アセンブリツール • ILSpy http://ilspy.net/ • 無償で使えておすすめ • IL以外にもC#やVBへ逆アセンブリ可能 • ildasm • .NET SDK標準ツール • C:¥Program Files¥Microsoft SDKs¥Windows¥... に入っている • .NET Reflector • 有償($95)
  18. 18. API • 標準API • System.Reflectoin.Emit(.NET 2.0/1.1~) • Sysytem.CodeDom(.NET 2.0/1.1~) • System.Linq.Expressions(実質.NET 4.0~) • 外部ライブラリ • Mono.Cecil • IKVM.Reflection • CLR Profiling API
  19. 19. System.Reflection.Emit(.NET 2.0/1.1~) • IL生成の基本的なAPI • 動的に生成したILコードは即座に実行可能 • 低レベルAPIなのでプログラムが煩雑になる • OpCodeを1つずつ埋め込むことが必要 • .NET 3.5までの環境ではこれを使うことになる
  20. 20. System.Reflection.Emitのコード例 Hello Worldを表示するだけのEXEを生成 var appDomain = AppDomain.CurrentDomain; var assemblyBuilder = appDomain.DefineDynamicAssembly( new AssemblyName("Test"), AssemblyBuilderAccess.RunAndSave); var moduleBuilder = assemblyBuilder.DefineDynamicModule("Test.exe"); var typeBuilder = moduleBuilder.DefineType("Program", TypeAttributes.Class); var methodBuilder = typeBuilder.DefineMethod("Main", MethodAttributes.Static); methodBuilder.SetParameters(typeof(string[])); var il = methodBuilder.GetILGenerator(); var write = typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }); il.Emit(OpCodes.Ldstr, "Hello World"); il.Emit(OpCodes.Call, write); il.Emit(OpCodes.Ret); typeBuilder.CreateType(); assemblyBuilder.SetEntryPoint(methodBuilder); assemblyBuilder.Save("Test.exe");
  21. 21. System.CodeDom(.NET 2.0/1.1~) • C#やVBのコンパイラAPI • 即時実行可能(スクリプト的に利用できる) • Assemblyの出力可能 • System.Reflection.Emitの置き換えにはらなない • コンパイル時にオーバーヘッドが発生する • 生成するILを動的に変更したい場合、文字列として ソースコードを組み立てる必要がある
  22. 22. System.Linq.Expressions(実質.NET 4.0~) • Expression Tree(式木) • 式木とはいうものの、構文木(if文, for文など)もカバー • System.Reflection.Emitよりも簡単 • ラムダ式からExpression Treeを構築できる • Expression Treeを走査したり改変もできる • .NET 4.0以降でIL生成といえばこれ Expression<Action> exp = () => { Console.WriteLine("Hello World"); }
  23. 23. System.Linq.Expressionsのコード例 Hello Worldを表示するだけのEXEを生成 var appDomain = AppDomain.CurrentDomain; var assemblyBuilder = appDomain.DefineDynamicAssembly( new AssemblyName("Test"), AssemblyBuilderAccess.RunAndSave); var moduleBuilder = assemblyBuilder.DefineDynamicModule("Test.exe"); var typeBuilder = moduleBuilder.DefineType("Program", TypeAttributes.Class); var methodBuilder = typeBuilder.DefineMethod("Main", MethodAttributes.Static); methodBuilder.SetParameters(typeof(string[])); Expression<Action> exp = () => Console.WriteLine("Hello World"); exp.CompileToMethod(methodBuilder); typeBuilder.CreateType(); assemblyBuilder.SetEntryPoint(methodBuilder); assemblyBuilder.Save("Test.exe");
  24. 24. System.Linq.Expressionsのコード例 ラムダの書き換え(メッセージ出力を追加) class SampleVisitor : ExpressionVisitor { protected override Expression VisitLambda<T>(Expression<T> node) { Expression<Action> post = () => Console.WriteLine("post"); return Expression.Lambda<Action>(Expression.Block(new Expression[] { Expression.Invoke(node), Expression.Invoke(post) })); } } var visitor = new SampleVisitor(); var result = visitor.Visit(exp); var func = ((Expression<Action>)result).Compile(); func.Invoke();
  25. 25. Mono.Cecil • Assembly(EXE, DLL)のRead/Write • 生成時にデバッグシンボルの出力も可能 • Assemblyの解析、処理の挿入や改変 • リソースの挿入、差し替え • 生成したILの即時実行は不可能 • Assembly保存→DLLとして読み込みなら可能 • コンパイラ, AOP, IL to JavaScript, ...
  26. 26. Mono.Cecilのコード例 Hello Worldを表示するだけのEXEを生成 var assembly = AssemblyDefinition.CreateAssembly( new AssemblyNameDefinition("Test", new Version()), "Test.exe", ModuleKind.Console); var module = assembly.MainModule; var type = module.Types.First(); var method = new MethodDefinition( "Main", MethodAttributes.Static, module.Import(typeof(void))); var il = method.Body.GetILProcessor(); var write = typeof(Console).GetMethod("WriteLine", new[] { typeof(object) }); il.Emit(OpCodes.Ldstr, "HelloWorld"); il.Emit(OpCodes.Call, module.Import(write)); il.Emit(OpCodes.Ret); type.Methods.Add(method); assembly.EntryPoint = method; assembly.Write("Test.exe");
  27. 27. Mono.Cecilのコード例 全メソッドの末尾(retの前)にメッセージ出力を追加 var assembly = AssemblyDefinition.ReadAssembly("Test.exe"); var write = typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }); assembly.Modules .SelectMany(x => x.Types) .SelectMany(x => x.Methods) .ToList() .ForEach(method => { var il = method.Body.GetILProcessor(); var ldstr = il.Create(OpCodes.Ldstr, "Finish"); var call = il.Create(OpCodes.Call, method.Module.Import(write)); method.Body.Instructions .Where(x => x.OpCode == OpCodes.Ret) .ToList() .ForEach(x => { il.InsertBefore(x, ldstr); il.InsertBefore(x, call); }); }); assembly.Write("Test-Mod.exe"); Assembly内の全メソッドを取得 Console.WriteLine("Finish");を 挿入
  28. 28. IKVM.Reflection • Assembly(EXE, DLL)のRead/Write • 生成時にデバッグシンボルの出力も可能 • System.Reflectionに似たAPI • 生成したILの即時実行は不可能 • Assembly保存→DLLとして読み込みなら可能 • Mono C#コンパイラのバックエンドで採用
  29. 29. CLR Profiling API • .NET界でも最上級レベルの黒魔術 • 実行中に全APIをフックして処理の差し替えが可能 • 私は使ったことはありません • 参考資料 • C# 動的メソッド入れ替え - Apply a monkey patch to any static languages on CLR - http://urasandesu.blogspot.jp/2011/10/c-apply-monkey-patch-to-any-static.html
  30. 30. Windows Store Appsでの IL生成 photo: http://www.flickr.com/photos/mr_o/8028197750/, CC BY-NC-SA 2.0
  31. 31. .NET for Windows Store Apps 同じ.NETでも、Windows Store AppsとDesktopの APIは大きく異なる • 名前空間の整理、スリム化 • 非同期前提 • セキュリティ的に危険なAPIを徹底削除
  32. 32. IL関連のAPI • System.Linq.Expressionsのみが存在 • System.Reflection.Emitは削除 • Assemblyを生成/ロードするAPIは削除 • ILを生成してもローカルキャッシュできない • Assemblyは所詮バイト列なのでどうにかなる • ロードできないので、生成しても自分で使えない • Windows RTで自作.NETアプリが動作すれば…
  33. 33. まとめ photo: http://www.flickr.com/photos/johncline/108863343/, CC BY-NC-ND 2.0
  34. 34. まとめ 極力、IL生成は避ける。
  35. 35. どうしてもIL生成したいのなら • とりあえずSystem.Linq.Expressions • 自由度が高く、APIもわかりやすい • Mono.Cecilを使えばAssemblyの改変が捗る • 採用しているOSSも多いので参考にしやすい
  36. 36. ご清聴ありがとうございました

×