• Like
動的なILの生成と編集
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

動的なILの生成と編集

  • 2,538 views
Published

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

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

Published in Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
2,538
On SlideShare
0
From Embeds
0
Number of Embeds
6

Actions

Shares
Downloads
9
Comments
0
Likes
8

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. 動的なILの生成/編集 terurou 2013/07/20
  • 2. 目次 • 自己紹介 • IL生成の予備知識 • ILの生成方法 • Windows Store AppsでのIL生成 • まとめ
  • 3. 自己紹介
  • 4. 7月から無職してます。
  • 5. 技術領域 • Haxe/JavaScriptとかWindows Store Appsとか WPFとかSilverlightとかできます。 • Haxe/JavaScriptは「なごやまつり」で話す予定です。 • ギョーム系DataGridの実装に定評があります。 • たぶんシステムアーキテクチャ設計が本業です。 • 最近はF#でWebSocketサーバを試作してました。
  • 6. コミュニティ活動 - DSTokaiとNGK • DSTokai : 東海地方のメタコミュニティ • IT系イベントカレンダーとコミュニティ連絡用ML • http://go.dstokai.info/ • NGK : なごや ごうどう こんしんかい • 毎年忘年会を開催しています • 一昨年は100人、去年は120人 • ちなみに今年は12/7に開催予定 • 花見はここ数年サボってます
  • 7. ILと私 • IronPythonとSilverlight2でR&D(3年前) • Silverlight上でScalaアプリを実行(2年前) • Silverlightアプリを動的生成(1年半前) • Store AppsからDesktop用EXE生成(1年前) • その他、.NETのお仕事で仕方なく…
  • 8. IL生成の予備知識 photo: http://www.flickr.com/photos/seditiouscanary/1279041211/, CC BY-NC-ND 2.0
  • 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. .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. 動的なILの生成 .NETにはILを動的(アプリ実行時)に生成するAPIが 標準で備わっています。 • 静的な型やメソッドの作成 • 生成したILの即時実行 • Assembly(EXE, DLL)の生成・保存 • 既存APIの編集(Monkey Patching)は不可能 • 別の手段を使えば編集することも可能
  • 12. IL生成の利用例 • 処理の高速化 • 動的メソッドコール(Reflection) • シリアライザ(動的に静的型を生成) • 透過プロキシやMockの内部実装 • アスペクト指向/Weaving • 自作コンパイラのバックエンド
  • 13. 本当にIL生成が必要か? • IL生成は黒魔術 • 利用例で紹介した通り、必要な個所は限定される • 問題点も多い • 低レベルAPI • 可読性やメンテナンス性の低下 • プログラミング言語の型制約の無視できる • 型安全ではなくなり、品質保証にコストがかかる
  • 14. IL生成の前に検討すべきもの • デザインパターンやIoC Container • コードの書き方で逃げられないか? • T4テンプレート(自動コード生成) • コンパイル前にどうにかならないか? • System.Dynamic(C# 4.0のdynamic型) • 動的プロパティや動的メソッドコール等で利用 • 内部的には実行時に自動でILを生成している • 下手に手書きでILを生成するよりも高速 • F#(高度な型システム, TypeProvider) • C#やVBの型システムが貧弱だからなのでは??
  • 15. ILの生成方法
  • 16. IL生成の基本的な手順 1. C#でひな形コードを書いてビルド 2. Assemblyを逆アセンブリ • ildasmやILSpy等を利用する 3. 逆アセンブリした結果を見ながらコードを書く
  • 17. 逆アセンブリツール • ILSpy http://ilspy.net/ • 無償で使えておすすめ • IL以外にもC#やVBへ逆アセンブリ可能 • ildasm • .NET SDK標準ツール • C:¥Program Files¥Microsoft SDKs¥Windows¥... に入っている • .NET Reflector • 有償($95)
  • 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. System.Reflection.Emit(.NET 2.0/1.1~) • IL生成の基本的なAPI • 動的に生成したILコードは即座に実行可能 • 低レベルAPIなのでプログラムが煩雑になる • OpCodeを1つずつ埋め込むことが必要 • .NET 3.5までの環境ではこれを使うことになる
  • 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. System.CodeDom(.NET 2.0/1.1~) • C#やVBのコンパイラAPI • 即時実行可能(スクリプト的に利用できる) • Assemblyの出力可能 • System.Reflection.Emitの置き換えにはらなない • コンパイル時にオーバーヘッドが発生する • 生成するILを動的に変更したい場合、文字列として ソースコードを組み立てる必要がある
  • 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. 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. 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. Mono.Cecil • Assembly(EXE, DLL)のRead/Write • 生成時にデバッグシンボルの出力も可能 • Assemblyの解析、処理の挿入や改変 • リソースの挿入、差し替え • 生成したILの即時実行は不可能 • Assembly保存→DLLとして読み込みなら可能 • コンパイラ, AOP, IL to JavaScript, ...
  • 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. 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. IKVM.Reflection • Assembly(EXE, DLL)のRead/Write • 生成時にデバッグシンボルの出力も可能 • System.Reflectionに似たAPI • 生成したILの即時実行は不可能 • Assembly保存→DLLとして読み込みなら可能 • Mono C#コンパイラのバックエンドで採用
  • 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. Windows Store Appsでの IL生成 photo: http://www.flickr.com/photos/mr_o/8028197750/, CC BY-NC-SA 2.0
  • 31. .NET for Windows Store Apps 同じ.NETでも、Windows Store AppsとDesktopの APIは大きく異なる • 名前空間の整理、スリム化 • 非同期前提 • セキュリティ的に危険なAPIを徹底削除
  • 32. IL関連のAPI • System.Linq.Expressionsのみが存在 • System.Reflection.Emitは削除 • Assemblyを生成/ロードするAPIは削除 • ILを生成してもローカルキャッシュできない • Assemblyは所詮バイト列なのでどうにかなる • ロードできないので、生成しても自分で使えない • Windows RTで自作.NETアプリが動作すれば…
  • 33. まとめ photo: http://www.flickr.com/photos/johncline/108863343/, CC BY-NC-ND 2.0
  • 34. まとめ 極力、IL生成は避ける。
  • 35. どうしてもIL生成したいのなら • とりあえずSystem.Linq.Expressions • 自由度が高く、APIもわかりやすい • Mono.Cecilを使えばAssemblyの改変が捗る • 採用しているOSSも多いので参考にしやすい
  • 36. ご清聴ありがとうございました