2016-03-14 13 views
5

Attualmente sto cercando di utilizzare la funzionalità AST introdotta in PowerShell 3.0 per modificare uno ScriptBlock. Il mio requisito è che tutti i parametri nel blocco parametri di ScriptBlock abbiano un attributo [Parameter(Mandatory)].Modifica ed estensioni AST PowerShell

In pratica il codice dovrebbe modificare questo:

Param([string]$x) 

Write-Host $x 

a questo:

Param([Parameter(Mandatory)][string]$x) 

Write-Host $x 

Tuttavia, mi sono imbattuto in un problema quando si aggiunge che nuovo attributo, dal momento che prevede un IScriptExtent e io non sono certo come dovrei creare un nuovo IScriptExtent.

Come posso creare una nuova estensione di script? Quali valori posso usare per la posizione? Devo cambiare la posizione di tutte le seguenti estensioni?

Ho provato a riutilizzare l'estensione di ciascun parametro che sto modificando, ma sfortunatamente questo non sembra fornire i risultati che dovrebbe (ad esempio quando sto chiamando ToString sul modificato non vedo alcuna modifica).

La mia implementazione fino ad ora si basa su ICustomAstVisitor trovato here.

Il metodo più importante aspetto:

public object VisitParameter(ParameterAst parameterAst) 
{ 
    var newName = VisitElement(parameterAst.Name); 

    var extent = // What to do here? 

    var mandatoryArg = new AttributeAst(extent, new ReflectionTypeName(typeof (ParameterAttribute)), 
     new ExpressionAst[0], 
     new[] {new NamedAttributeArgumentAst(extent, "Mandatory", new ConstantExpressionAst(extent, true), true)}); 

    var newAttributes = new[] {mandatoryArg}.Concat(VisitElements(parameterAst.Attributes)); 
    var newDefaultValue = VisitElement(parameterAst.DefaultValue); 
     return new ParameterAst(parameterAst.Extent, newName, newAttributes, newDefaultValue); 
} 

risposta

3

nomi che iniziano con I sono in genere interfacce. Non sono classi di cui si crea un'istanza, sono contratti di ordinamento che specificano che una particolare classe implementa un certo insieme noto di funzionalità.

Ad esempio, un [hashtable] strumenti IEnumerable. Ciò significa che tutto ciò che sa come lavorare con un'interfaccia IEnumerable e operare su quella classe; potresti creare la tua classe che implementa l'interfaccia e il codice che non avrebbe mai potuto conoscere della tua classe o quello che fa può ancora interagire con esso nel modo in cui lo definisce IEnumerable (che in questo caso è un modo per scorrere su di esso).

Quindi, quando una funzione dichiara un parametro con un tipo di interfaccia, non sta cercando alcuna classe specifica, sta cercando qualsiasi classe che implementa tale interfaccia.

Il passaggio successivo è quindi individuare quali tipi implementano tale interfaccia. Ecco un po 'di codice PowerShell che ho usato per trovare quelli:

[System.AppDomain]::CurrentDomain.GetAssemblies().GetTypes() | Where-Object { 
    [System.Management.Automation.Language.IScriptExtent].IsAssignableFrom($_) 
} 

Da questo, possiamo vedere quanto segue:

IsPublic IsSerial Name          BaseType              
-------- -------- ----          --------              
True  False IScriptExtent                       
False False InternalScriptExtent      System.Object            
False False EmptyScriptExtent      System.Object            
True  False ScriptExtent        System.Object            

Il primo elenco sono l'interfaccia stessa. Degli altri tre, due di loro non sono pubblici, quindi lascia solo ScriptExtent.

È possibile creare uno di questi con New-Object ma è necessario fornire le posizioni di inizio e fine come oggetti [ScriptPosition]. Non sono del tutto sicuro di ciò che dovrebbero essere senza vedere più del tuo codice.

+0

so che cosa l'interfaccia è, la mia domanda ruota attorno a me non del tutto essere in grado di capire come estensioni funzionano in PowerShell per la creazione di un nuovo codice (ci sono molti esempi di persone modificare il codice e il riutilizzo di estensioni, ma io non è stato possibile trovare alcun esempio in cui le persone hanno creato un nuovo codice). – chrischu

+1

@chrischu non è abbastanza chiaro dalla tua domanda che tu abbia familiarità con le interfacce dato che ti chiedi come creare un nuovo 'IScriptExtent', quindi ho ritenuto che fosse meglio errare sul lato sicuro e spiegare, soprattutto perché potrebbe essere utile altri visitatori che trovano la tua domanda ma non sanno cosa sia un'interfaccia. Potresti anche considerare di includere nella tua domanda ciò che hai provato fino ad ora. – briantist

3

L'estensione script viene utilizzato principalmente per la segnalazione degli errori, ma viene utilizzato anche per il debugging (., Ad esempio, l'impostazione di una linea breakpoint)

In generale, le opzioni per lo script di sintesi (come il tuo esempio) sono:

  • riutilizzare un AST esistente, presumibilmente nei pressi di/relativo al ast si sta aggiungendo
  • utilizzare un ast vuota (in pratica creare istanze di ScriptExtent e ScriptPosition con nessun file, riga vuota)
  • creare un sintetico misura che aiuta a eseguire il debug in qualche modo, magari con qualche contenuto speciale

Nel tuo esempio, uno dei precedenti è adatto. La seconda opzione è la più semplice. La terza opzione è solo una variante del secondo, ma puoi impostare il contenuto su qualcosa di utile, ad es.

<#Generated: [Parameter(Mandatory)] #> 
+0

Interessante. Sebbene abbia un problema, chiamare ToString() sull'AST modificato restituisce il vecchio codice e il mio pensiero è che ciò avvenga a causa delle vecchie estensioni. C'è un altro buon modo per trasformare l'AST di nuovo nel codice sorgente che non si basa sul fatto che le estensioni siano corrette? – chrischu

+0

Non così facilmente - una stampante carina è una cosa perfettamente ragionevole da implementare basata su Ast, ma non ne sono a conoscenza, tranne un codice di esempio che ho scritto molto tempo fa, che in realtà non fa un ottimo lavoro formattazione. –

+2

Beh, la "bella stampante" è l'intero problema: non ho nemmeno bisogno che il codice sia bello, ho solo bisogno di ottenere il codice dopo aver modificato l'AST e questo non sembra funzionare senza fornire estensioni "corrette". In un certo senso sembra anche che sarebbe più semplice usare la modifica della stringa per modificare il codice invece di dilettarsi con l'AST che sarebbe un vero peccato. – chrischu