Modification and Extents PowerShell AST

I'm currently trying to use the AST functionality introduced in PowerShell 3.0 to modify ScriptBlock. My requirement is that all parameters in the ScriptBlock parameter block get the [Parameter(Mandatory)] attribute.

Basically the code should change this:

 Param([string]$x) Write-Host $x 

:

 Param([Parameter(Mandatory)][string]$x) Write-Host $x 

However, when adding this new attribute, I ran into a problem, as it expects IScriptExtent , and I'm not sure how to create a new IScriptExtent .

How to create a new script size? What values ​​can I use for a line item? Do I need to reposition all of the following extents?

I tried to just reuse the size of each parameter that I am changing, but unfortunately this does not seem to give the results it should (for example, when I call ToString in the modified ScriptBlock , I don't see any changes).

My implementation is still based on ICustomAstVisitor found here .

The most important method is as follows:

 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); } 
+5
source share
2 answers

Names starting with I are usually interfaces. They are not the classes in which you instantiate, they are contracts that determine that a particular class implements a certain known set of functionality.

For example, [hashtable] implements IEnumerable . This means that everyone knows how to work with the IEnumerable interface and work with this class; you can create your own class that implements the interface, and code that you could never know about your class or what it does can still interact with it as defined by IEnumerable (which in this case is a way to iterate over it )

So, when a function declares a parameter with an interface type, it is not looking for any particular class, it is looking for any class that implements this interface.

The next step is to find out which types implement this interface. Here are some PowerShell codes I used to search:

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

From this one can see the following:

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

The first list is the interface itself. Of the other three, two of them are not public, so just ScriptExtent .

You can create one of them using New-Object , but you need to specify the start and end positions as [ScriptPosition] . I'm not quite sure what this should be without seeing more of your code.

+3
source

The degree of script is used mainly for error messages, but is also used for debugging (for example, setting a line breakpoint).

In general, synthesized script options (like your example):

  • reusing an existing aster presumably close / related to the one you add
  • use an empty star (basically create instances of ScriptExtent and ScriptPosition without a file, an empty string)
  • create a synthetic degree that helps in debugging in some way, perhaps with some special content

In your example, any of the above options are appropriate. The second option is the simplest. The third option is just a variant of the second, but you should install the content on something useful, for example.

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

Source: https://habr.com/ru/post/1245033/


All Articles