ILSpyのエンジンで逆コンパイルしよう
高校三年生になりました。降参です。以上です。
とかいう話をしましたが、あのあともはや完全に 1 から作りなおしに近い形で大幅アップデートしました(といってももう 1 週間以上前の話ですが)。あの記事で挙げたこれからの予定ですが、まさかの「アイコンほしい」以外をすべて実現してしまいました。その中でもイチオシ機能はアセンブリブラウザの実装です。
我ながら便利すぎて惚れてまうんですが、この C# コードの吐き出しの仕方を紹介します。
ILSpy のソースコードを入手
逆コンパイルには ILSpy 内にある ICSharpCode.Decompiler を使いました。これ単体でも十分使えるライブラリなのですが、残念ながら単体で NuGet に公開されていたりはしないので、 ILSpy をダウンロードして dll を取得するか、ソースコードをダウンロードしてくる必要があります。
NRefactory と Mono.Cecil に依存しているのでそれらも忘れないでください。
いざ逆コンパイル
なんとなく CoreTweet.Streaming.StreamingApi
でやってみました。
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Ast; using Mono.Cecil; var modDef = ModuleDefinition.ReadModule("CoreTweet.dll"); TypeDefinition typeDef = modDef.GetType("CoreTweet.Streaming.StreamingApi"); var builder = new AstBuilder(new DecompilerContext(modDef)); builder.AddType(typeDef); builder.RunTransformations(); var output = new PlainTextOutput(); builder.GenerateCode(output); Console.Write(output.ToString());
Cecil でアセンブリを読み込み、それを ICSharpCode.Decompiler.Ast.AstBuilder
に入れてやることで C# コードが生成されます。結果はこんな感じになります。
using CoreTweet.Core; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Net; namespace CoreTweet.Streaming { public class StreamingApi : ApiProviderBase { protected internal StreamingApi(TokensBase tokens) : base(tokens) { } private IEnumerable<string> Connect(StreamingParameters parameters, MethodType type, string url) { using (HttpWebResponse httpWebResponse = base.Tokens.SendStreamingRequest(type, url, parameters.Parameters)) { using (StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream())) { foreach (string current in from x in streamReader.EnumerateLines() where !string.IsNullOrEmpty(x) select x) { yield return current; } } } yield break; } public IEnumerable<StreamingMessage> StartStream(StreamingType type, StreamingParameters parameters = null) { if (parameters == null) { parameters = new StreamingParameters(new Expression<Func<string, object>>[0]); } string url = (type == StreamingType.User) ? "https://userstream.twitter.com/1.1/user.json" : ((type == StreamingType.Site) ? " https://sitestream.twitter.com/1.1/site.json " : ((type == StreamingType.Filter) ? "https://stream.twitter.com/1.1/statuses/filter.json" : ((type == StreamingType.Sample) ? "https://stream.twitter.com/1.1/statuses/sample.json" : ((type == StreamingType.Firehose) ? "https://stream.twitter.com/1.1/statuses/firehose.json" : "")))); IEnumerable<string> enumerable = from x in this.Connect(parameters, (type == StreamingType.Filter) ? MethodType.Post : MethodType.Get, url) where !string.IsNullOrEmpty(x) select x; foreach (string current in enumerable) { StreamingMessage streamingMessage; try { streamingMessage = StreamingMessage.Parse(current); } catch { streamingMessage = RawJsonMessage.Create(current); } yield return streamingMessage; } yield break; } } }
yield がちゃんと再現されてるのが感動しますね。
AstBuilder.DecompileMethodBodies
を false
にすると、 NuGetCalc Web でやっているように、メンバー一覧機能として利用できます。これは MonoDevelop のアセンブリブラウザで「サマリー」を指定したときに使われているものです。
そのほか、 DecompilerContext.Settings
で出力コードのスタイルを変更できます。
XML コメントを表示する
これは ICSharpCode.Decompiler では提供されていない機能なので、 ILSpy 自体のソースコードから取ってきます。必要なのは以下 3 ファイル
そして builder.RunTransformations();
のあとに
ICSharpCode.ILSpy.XmlDoc.AddXmlDocTransform.Run(builder.SyntaxTree);
を追加します。これだけ。これで ModuleDefinition の情報から XML ファイルを探しに行って AstBuilder に追加してくれます。
実行結果も書いておきたいところでしたが、 mono の XmlTextReader のバグによりぬるりで落ちるので書けませんでした。はい。 NuGetCalc Web では ReferenceSource に移行したバージョンを使っているのでちゃんと動いています。
まとめ
NuGet パッケージ化頼む。ていうか、てさ部何やってるの。