2014-11-21 26 views
5

Vorrei analizzare. Assiemi .Net indipendenti da C#, VB.NET o altro.
Conosco Roslyn e NRefactory ma sembrano funzionare solo sul livello del codice sorgente C#?
C'è anche il progetto "Common Compiler Infrastructure: Code Model and AST API" su CodePlex che dichiara di "supportare un modello di oggetto gerarchico che rappresenta blocchi di codice in una forma strutturata indipendente dalla lingua" che suona esattamente per quello che sto cercando.
Tuttavia, non sono in grado di trovare alcuna documentazione o codice utile che sia effettivamente in esecuzione.
Qualche consiglio su come archiviare questo?
Posso forse fare qualcosa Mono.Cecil?Ottieni AST da. Assemblaggio Net senza codice sorgente (codice IL)

risposta

0

Per quanto ne so, non è possibile costruire AST da binario (senza origini) poiché AST stesso generato dal parser come parte del processo di compilazione da fonti. Mono.Cecil non aiuta perché è possibile modificare solo opcode/metadati con essi, non analizzare l'assembly.

Ma dal momento che è .NET è possibile scaricare il codice IL da dll con l'aiuto di ildasm. Quindi è possibile passare sorgenti generate a qualsiasi parser con il dizionario CIL collegato e ottenere AST dal parser. Il problema è che per quanto ne so c'è solo una grammatica CIL pubblicamente disponibile per il parser, quindi non hai una scelta. E ECMA-355 è abbastanza grande quindi è una cattiva idea scrivere la tua grammatica. Così ti posso suggerire solo una soluzione: il montaggio

  1. Pass Ildasm.exe per ottenere CIL.
  2. Poi passare CIL per ANTLR v3 parser con this CIL grammatica cablato (si noti che è un po 'datato - grammatica creato il 2004 e più recenti specifiche CIL è il 2006, ma CIL in realtà non cambia di molto)
  3. Dopo di che si possono accedere gratuitamente AST generato da ANTLR

Nota che sarà necessario ANTLR non v3 v4, dal momento che la grammatica scritta per il 3 ° versione, ed è quasi impossibile portarlo a v4 senza una buona conoscenza della sintassi ANTLR.

Inoltre si può provare a guardare in nuove Microsoft ryujit fonti del compilatore a github (parte di CoreCLR) - Non sicuro che sia aiuta, ma in teoria il mosto contengono CIL grammatica e parser implementazioni poiché funziona con Codice CIL. Ma è scritto in CPP, ha un'enorme base di codice e manca di documentazione poiché è in fase di sviluppo attivo, quindi potrebbe essere più facile rimanere bloccato con ANTLR.

+0

Se si desidera solo ottenere l'IL, questo approccio è troppo convoluto. Usare Cecil sarà molto più semplice. – svick

0

Se si considera il file binario .net come un flusso di byte, si dovrebbe essere in grado di "analizzare" correttamente.

Basta scrivere una grammatica i cui token sono essenzialmente byte. Puoi certamente costruire un lexer/parser classico con quasi tutti gli strumenti di lexer/parser definendo il lexer per leggere singoli byte come token.

È quindi possibile creare l'AST utilizzando i macchinari di costruzione standard AST per il motore di analisi (da soli per YACC, automaticamente con ANTLR4).

Quello che scoprirete, ovviamente, è che "analizzare" non è abbastanza; dovrai ancora costruire tabelle di simboli ed eseguire analisi di controllo e flusso di dati se stai per fare un'analisi seria del codice corrispondente. Vedi il mio saggio su LifeAfterParsing.

È anche probabile che si debbano prendere in considerazione funzioni "distinte" che forniscono funzionalità di runtime chiave ai linguaggi di programmazione specifici che hanno effettivamente generato il codice CIL. E questi renderanno i tuoi analizzatori dipendenti dalla lingua. Sì, puoi ancora condividere la parte dell'analisi che funziona con CIL generico.

+0

Il formato di file binario .NET non si qualifica come privo di contesto (o sensibile al contesto), quindi né YACC né ANTLR saranno in grado di generare un parser per questo. –

+0

Dove violano c-free o c-sensitive? –

+0

1) La posizione di varie strutture all'interno del modulo è specificata con un RVA (indirizzo virtuale relativo), la ricerca dell'offset all'interno del file richiede la mappatura tramite la tabella delle sezioni. 2) I conteggi delle righe per ogni tabella vengono visualizzati prima di ognuno di essi, questi conteggi possono influire su determinate larghezze delle colonne. 3) La maggior parte delle DLL che ho esaminato collocano i metadati dopo i corpi delle funzioni (sebbene ciò non sia richiesto) quindi se stai leggendo solo in un modo forward (come con un parser generato), non saprai quando hai incontrato un metodo body fino a molto tempo dopo averlo passato. –

2

È possibile eseguire questa operazione ed è disponibile anche una (anche se piccola) example of this nell'origine di ILSpy.

var assembly = AssemblyDefinition.ReadAssembly("path/to/assembly.dll"); 
var astBuilder = new AstBuilder(new DecompilerContext(assembly.MainModule)); 
decompiler.AddAssembly(assembly); 
astBuilder.SyntaxTree... 
1

Il CCI Model Code è da qualche parte tra un disassemblatore IL e pieno C# decompilatore: dà il codice di qualche struttura (ad esempio if dichiarazioni ed espressioni), ma contiene anche alcune operazioni in bassa pila livello come push e pop.

CCI contiene un esempio che mostra questo: PeToText.

Ad esempio, per ottenere il codice del modello per il primo metodo del tipo Program (nel namespace globale), è possibile utilizzare il codice come questo:

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); 
} 

Per dimostrare, se si decompilare quanto sopra codice e vederlo con PeToText, si sta andando ad ottenere:

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(); 
    } 
} 

di nota sono tutte quelle dichiarazioni push, pop e dup e la condizione di caching lambda.

Problemi correlati