2016-03-14 21 views
5

Ich versuche derzeit, die AST-Funktionalität zu verwenden, die in PowerShell 3.0 eingeführt wird, um einen ScriptBlock zu ändern. Meine Voraussetzung ist, dass alle Parameter im Parameterblock des ScriptBlocks ein [Parameter(Mandatory)] Attribut erhalten.PowerShell AST-Änderung und Extents

Grundsätzlich sollte der Code dies ändern:

Param([string]$x) 

Write-Host $x 

dazu:

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

Write-Host $x 

Allerdings habe ich ein Problem lief, wenn das neue Attribut hinzufügen, da es eine IScriptExtent erwartet und ich bin nicht sicher wie ich ein neues IScriptExtent erstellen soll.

Wie kann ich einen neuen Skriptumfang erstellen? Welche Werte kann ich für die Position verwenden? Muss ich die Position aller folgenden Extents ändern?

Ich versuchte, nur das Ausmaß jedes Parameters, den ich verändere, wiederzuverwenden, aber leider scheint dies nicht zu den Ergebnissen zu führen, die es sollte (z.B. wenn ich ToString auf dem modifizierten ScriptBlock aufrufen sehe ich keine Änderungen).

Meine bisherige Implementierung basiert auf der ICustomAstVisitor gefunden here.

Die wichtigste Methode sieht wie folgt aus:

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

Antwort

3

Namen, die mit I beginnen typischerweise Schnittstellen. Es handelt sich nicht um Klassen, mit denen Sie eine Instanz erstellen, sondern um solche, die angeben, dass eine bestimmte Klasse eine bestimmte bekannte Funktionalität implementiert.

Zum Beispiel implementiert ein [hashtable]IEnumerable. Das bedeutet, dass alles, was mit einer IEnumerable Schnittstelle zu arbeiten weiß und auf dieser Klasse arbeiten kann; Sie könnten Ihre eigene Klasse erstellen, die die Schnittstelle implementiert, und Code, der niemals über Ihre Klasse hätte wissen können, oder was er tut, kann weiterhin mit ihm interagieren, wie IEnumerable definiert (was in diesem Fall eine Möglichkeit ist, darüber zu iterieren).

Wenn also eine Funktion einen Parameter mit einem Schnittstellentyp deklariert, sucht sie nicht nach einer bestimmten Klasse, sondern nach einer Klasse, die diese Schnittstelle implementiert.

Der nächste Schritt ist dann zu finden, welche Typen diese Schnittstelle implementieren. Hier einige Powershell-Code, den ich verwendet, um diejenigen zu finden:

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

Der erste Eintrag ist die Schnittstelle selbst:

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

Von diesem können wir folgendes sehen. Von den anderen drei sind zwei von ihnen nicht öffentlich, so dass nur ScriptExtent verlässt.

Sie können eine davon mit New-Object erstellen, aber Sie müssen die Start- und Endpositionen als [ScriptPosition] Objekte liefern. Ich bin nicht ganz sicher, was diese sein sollten, ohne mehr von deinem Code zu sehen.

+0

Ich weiß, was eine Schnittstelle ist, dreht ich meine Frage um mich nicht ganz in der Lage sein, herauszufinden, wie Ausdehnungen in Powershell arbeiten, wenn neuen Code zu schaffen (es Ausdehnungen viele Beispiele von Menschen, Code zu modifizieren und Wiederverwendung ist, aber ich konnte kein Beispiel finden, wo Leute neuen Code erstellt haben). – chrischu

+1

@chruschu Es ist nicht ganz klar aus deiner Frage, dass du mit Interfaces vertraut bist, da du fragst, wie man einen neuen 'IScriptExtent' erstellt, also war es das Beste, auf der sicheren Seite zu bleiben und zu erklären, besonders da es hilfreich sein kann andere Besucher, die Ihre Frage finden, aber nicht wissen, was eine Schnittstelle ist. Sie könnten auch in Ihre Frage einbeziehen, was Sie bisher versucht haben. – briantist

3

Das Skript Ausmaß in erster Linie für die Fehlerberichterstattung verwendet wird, sondern auch für die Fehlersuche verwendet wird (z. B. einen Zeilenhaltepunkt setzen)

Im Allgemeinen sind die Optionen für synthetisierte Skript (wie Ihr Beispiel):

  • Wiederverwendung eines bestehender ast, vermutlich in der Nähe von/im Zusammenhang mit dem ast Sie hinzufügen
  • einen leeren ast verwenden (im Grunde erstellen Instanzen von ScriptExtent und ScriptPosition ohne Datei, leere Zeile)
  • ein synthetischen schaffen Extent, das hilft, irgendwie zu debuggen, vielleicht mit etwas besonderem Inhalt

In Ihrem Beispiel sind alle der oben genannten geeignet. Die zweite Option ist die einfachste. Die dritte Option ist nur eine Variante der zweiten, aber Sie würden den Inhalt auf etwas Nützliches einstellen, z.

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

Interessant. Ein Problem, das ich habe, ist, dass das Aufrufen von ToString() auf dem modifizierten AST den alten Code zurückgibt und mein Denken war, dass das wegen der alten Extents passiert. Gibt es einen anderen guten Weg, um den AST zurück in Quellcode zu verwandeln, der nicht darauf angewiesen ist, dass die Extents korrekt sind? – chrischu

+0

Nicht so einfach - ein hübscher Drucker ist eine vollkommen vernünftige Sache, basierend auf dem Ast zu implementieren, aber ich bin mir dessen nicht bewusst, abgesehen von einem Beispielcode, den ich vor langer Zeit geschrieben habe und der eigentlich keinen guten Job macht Formatierung. –

+2

Nun, der "hübsche Drucker" ist das ganze Problem: Ich brauche nicht einmal den Code, um hübsch zu sein, ich brauche nur den Code NACH dem Ändern der AST und das scheint nicht zu funktionieren, ohne "korrekte" Extents bereitzustellen. In gewisser Weise scheint es sogar so, als wäre es einfacher, nur die Zeichenfolgenmodifikation zu verwenden, um den Code zu ändern, anstatt mit dem AST zu spielen, was sehr schade wäre. – chrischu