5

.Net アセンブリを分析して、C#、VB.NET などから言語に依存しないようにしたいと考えています。
Roslyn と NRefactory は知っていますが、C# ソース コード レベルでしか動作しないように見えますか?
また、CodePlex には「Common Compiler Infrastructure: Code Model and AST API」プロジェクトがあり、「言語に依存しない構造化された形式でコード ブロックを表す階層オブジェクト モデルをサポートする」と主張しています。
ただし、実際にこれを行っている有用なドキュメントやコードを見つけることができません。
これをアーカイブする方法について何かアドバイスはありますか?
Mono.Cecil は何かできるのでしょうか?

4

4 に答える 4

1

CCI コード モデルは、IL 逆アセンブラーと完全な C# 逆コンパイラーの中間に位置します。コードに何らかの構造 (ステートメントや式など) を提供しますが、やifなどの低レベルのスタック操作も含まれています。pushpop

CCI には、これを示すサンプルが含まれています: PeToText

たとえば、Program型の最初のメソッド (グローバル名前空間内) のコード モデルを取得するには、次のようなコードを使用できます。

string fileName = "whatever.exe";

using (var host = new PeReader.DefaultHost())
{
    var module = (IModule)host.LoadUnitFrom(fileName);
    var type = (ITypeDefinition)module.UnitNamespaceRoot.Members
        .Single(m => m.Name.Value == "Program");
    var method = (IMethodDefinition)type.Members.First();
    var methodBody = new SourceMethodBody(method.Body, host, null, null);
}

上記のコードを逆コンパイルして PeToText を使用して表示すると、次のようになります。

Microsoft.Cci.ITypeDefinition local_3;
Microsoft.Cci.ILToCodeModel.SourceMethodBody local_5;
string local_0 = "C:\\code\\tmp\\nuget tmp 2015\\bin\\Debug\\nuget tmp 2015.exe";
Microsoft.Cci.PeReader.DefaultHost local_1 = new Microsoft.Cci.PeReader.DefaultHost();
try
{
    push (Microsoft.Cci.IModule)local_1.LoadUnitFrom(local_0).UnitNamespaceRoot.Members;
    push Program.<>c.<>9__0_0;
    if (dup == default(System.Func<Microsoft.Cci.INamespaceMember, bool>))
    {
        pop;
        push Program.<>c.<>9.<Main0>b__0_0;
        Program.<>c.<>9__0_0 = dup;
    }
    local_3 = (Microsoft.Cci.ITypeDefinition)System.Linq.Enumerable.Single<Microsoft.Cci.INamespaceMember>(pop, pop);
    local_5 = new Microsoft.Cci.ILToCodeModel.SourceMethodBody((Microsoft.Cci.IMethodDefinition)System.Linq.Enumerable.First<Microsoft.Cci.ITypeDefinitionMember>(local_3.Members).Body, local_1, (Microsoft.Cci.ISourceLocationProvider)null, (Microsoft.Cci.ILocalScopeProvider)null, 0);
}
finally
{
    if (local_1 != default(Microsoft.Cci.PeReader.DefaultHost))
    {
        local_1.Dispose();
    }
}

注目すべきは、すべてのpush, popanddupステートメントとラムダ キャッシング条件です。

于 2015-05-16T15:07:37.410 に答える
0

私の知る限り、AST 自体はソースからのコンパイル プロセスの一部としてパーサーによって生成されるため、バイナリ (ソースなし) から AST を構築することはできません。Mono.Cecil は、オペコード/メタデータのみを変更でき、アセンブリを分析できないため、役に立ちません。

しかし、これは .NET であるため、ildasm を使用して dll から IL コードをダンプできます。次に、生成されたソースを CIL ディクショナリが接続された任意のパーサーに渡し、パーサーから AST を取得できます。問題は、私が知る限り、公開されているパーサー用の CIL 文法は 1 つしかないため、選択の余地がないことです。また、ECMA-355 は十分に大きいため、独自の文法を作成することはお勧めできません。したがって、私が提案できる解決策は 1 つだけです。

  1. アセンブリを ildasm.exe に渡して CIL を取得します。
  2. 次に、このCIL 文法を接続して、CIL をANTLR v3パーサーに渡します (少し古くなっていることに注意してください。文法は 2004 年に作成され、最新の CIL 仕様は 2006 年ですが、CIL は実際にはそれほど変更されていません)。
  3. その後、ANTLR が生成した AST に自由にアクセスできます。

v4 ではなく ANTLR v3 が必要になることに注意してください。文法は第 3 バージョン用に記述されており、ANTLR 構文の十分な知識がなければ v4 に移植することはほとんど不可能です。

また、github (CoreCLR の一部) で新しい Microsoft ryujitコンパイラ ソースを調べてみることもできます。それが役立つかどうかはわかりませんが、理論的には、CIL コードで動作するため、CIL 文法とパーサーの実装が含まれている必要があります。しかし、それは CPP で書かれており、膨大なコード ベースを持ち、活発な開発段階にあるためドキュメントが不足しているため、ANTLR に固執する方が簡単かもしれません。

于 2015-02-22T00:37:17.850 に答える