【问题标题】:PowerShell AST Modification and ExtentsPowerShell AST 修改和范围
【发布时间】:2016-03-14 17:17:45
【问题描述】:

我目前正在尝试使用 PowerShell 3.0 中引入的 AST 功能来修改 ScriptBlock。我的要求是ScriptBlock的参数块中的所有参数都得到一个[Parameter(Mandatory)]属性。

基本上代码应该修改这个:

Param([string]$x)

Write-Host $x

到这里:

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

Write-Host $x

但是,我在添加新属性时遇到了问题,因为它需要一个 IScriptExtent,而我不知道应该如何创建一个新的 IScriptExtent

如何创建新的脚本范围?我可以为该职位使用什么值?我是否必须更改所有以下范围的位置?

我尝试仅重用我正在修改的每个参数的范围,但不幸的是,这似乎并没有产生应有的结果(例如,当我在修改后的 ScriptBlock 上调用 ToString 时,我没有看到任何变化)。

到目前为止,我的实现是基于 ICustomAstVisitor 找到的 here

最重要的方法如下所示:

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

【问题讨论】:

    标签: powershell abstract-syntax-tree extent


    【解决方案1】:

    I 开头的名称通常是接口。它们不是您创建实例的类,它们是指定特定类实现特定已知功能集的各种契约。

    例如,[hashtable] 实现 IEnumerable。这意味着任何知道如何使用IEnumerable 接口并对该类进行操作的东西;您可以创建自己的实现接口的类,并且永远不会知道您的类或它的作用的代码仍然可以以IEnumerable 定义的方式与它交互(在这种情况下,这是一种迭代它的方式)。

    因此,当函数声明具有接口类型的参数时,它不是在寻找任何一个特定的类,而是在寻找实现该接口的任何类。

    下一步是找出实现该接口的类型。这是我用来查找这些的一些 PowerShell 代码:

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

    由此,我们可以看到以下内容:

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

    第一个清单是界面本身。另外三个中,有两个是不公开的,所以就留下ScriptExtent

    您可以使用New-Object 创建其中之一,但您需要将开始和结束位置提供为[ScriptPosition] 对象。如果没有看到更多代码,我不完全确定这些应该是什么。

    【讨论】:

    • 我知道接口是什么,我的问题是围绕着我不太能弄清楚在创建新代码时在 PowerShell 中的工作方式(有很多人修改代码和重用范围的例子,但我找不到任何人们创建新代码的例子)。
    • @chrischu 从你的问题中不太清楚你是否熟悉接口,因为你问如何创建一个新的IScriptExtent 所以我觉得最好在安全方面犯错并解释一下,特别是因为它可能对找到您的问题但知道界面是什么的其他访问者有所帮助。您也可以考虑在您的问题中包含您迄今为止尝试过的内容。
    【解决方案2】:

    脚本范围主要用于错误报告,但也用于调试(例如,设置行断点。)

    一般来说,合成脚本的选项(如您的示例)是:

    • 重用现有的 ast,可能与您正在添加的 ast 接近/相关
    • 使用空 ast(基本上是创建 ScriptExtent 和 ScriptPosition 的实例,没有文件,空行)
    • 创建一个合成范围,以某种方式帮助调试,可能包含一些特殊内容

    在您的示例中,以上任何一种都适合。第二个选项是最简单的。第三个选项只是第二个的变体,但您可以将内容设置为有用的内容,例如

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

    【讨论】:

    • 有趣。虽然我遇到的一个问题是,在修改后的 AST 上调用 ToString() 会返回旧代码,而我的想法是,这是由于旧范围而发生的。是否有另一种好方法可以将 AST 转回不依赖于正确范围的源代码?
    • 不是那么容易 - 一个漂亮的打印机是一个基于 Ast 实现的完全合理的东西,但我不知道有一个,除了我很久以前写的一些示例代码实际上没有'格式化工作做得不好。
    • 好吧,“漂亮的打印机”是整个问题:我什至不需要漂​​亮的代码,我只需要在修改 AST 之后获取代码,这似乎无法正常工作提供“正确”的范围。在某种程度上,使用字符串修改来修改代码似乎更容易,而不是涉足 AST,这将是相当可耻的。
    • @chrischu 您找到解决方案了吗?我现在也有同样的困境。
    • @milquetoastable 不,很遗憾我没有。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-07
    • 1970-01-01
    • 2017-11-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多