# encoding: ascii
# api: powershell
# title: ASTSplatting
# description: Another fun script that used PowerShell v3 ASTs. Have fun!
# version: 3.0
# type: function
# author: bielawb
# license: CC0
# function: Invoke-Static
# x-poshcode-id: 4151
# x-archived: 2017-03-12T05:21:38
# x-published: 2014-05-10T22:19:00
#
#
#requires -version 3.0
function Invoke-Static {
<#
.Synopsis
Invokes static methods with pseudo-splatting.
.Description
This function is using Abstract Syntax Tree to parse code passed.
Any [YourType]::StaticMethod($Arguments) call will be inspected.
If $Arguments can be used as hashtable - function will attempt to 'splat' it.
.Example
$Power = @{ x = 2; y = 3 }
Invoke-Static -ScriptBlock { [math]::Pow($Power) }
8
Keys of passed hashtable will be matched agains overloads for System.Math static method Pow.
Because keys match arguments (x, y) - function work as expected
.Example
$Param = @{ input = 'This-Is-Only-Test'; pattern = '-' }
Invoke-Static '[regex]::Split($Param)'
This
Is
Only
Test
Using parameter set with expression you can simply pass whole expression as a string.
.Example
$CheckFeb = @{ year = 2010; month = 2 }
^ [DateTime]::DaysInMonth($CheckFeb)
28
Same syntax without quoting will cause $CheckFeb to be another positional parameter.
This syntax is possible only if defaul function alias (^) is defined.
#>
[CmdletBinding(
DefaultParameterSetName = 'script'
)]
param (
# ScriptBlock that will be processed
[Parameter(
ParameterSetName = 'script',
Position = 0,
Mandatory
)]
[scriptblock]$ScriptBlock,
# Expression (partial or complete) that will be parsed
[Parameter(
ParameterSetName = 'string',
Position = 0,
Mandatory
)]
[string]$Expression,
# Argument to static function that will be used
[Parameter(
ParameterSetName = 'string',
Position = 1
)]$Splatted
)
if ($Expression) {
if ($Splatted) {
$Expression = "{0}({1})" -f $Expression, '$Splatted'
}
$ScriptBlock = [scriptblock]::Create($Expression)
}
foreach ($Invoke in $ScriptBlock.Ast.FindAll({
$args[0] -is [Management.Automation.Language.InvokeMemberExpressionAst] -and
$args[0].Static -and
$args[0].Arguments.Count -eq 1 -and
$args[0].Expression -is [Management.Automation.Language.TypeExpressionAst]
}, $false)) {
try {
$Type = [type]$Invoke.Expression.TypeName.FullName
$Method = $Invoke.Member.Value
$Name = $Invoke.Arguments.VariablePath.UserPath -replace '\w+:'
$Argument = Get-Variable -Name $Name -ValueOnly
if ($Hash = $Argument -as [hashtable]) {
$Selected = $Type.GetMethods() |
where {
$_.Name -eq $Method -and (
$_.GetParameters().Name.Count -eq
$Hash.Keys.Count ) -and (
$Hash.Keys.Count -eq
($_.GetParameters() |
where { $_.Name -in $Hash.Keys }).Count
)
}
foreach ($Item in $Selected) {
$ParamList = New-Object System.Collections.ArrayList
$Item.GetParameters() | sort Position |
ForEach-Object {
$ParamList.Add($Argument[$_.Name] -as $_.ParameterType) | Out-Null
}
$Item.Invoke($Item,$ParamList.ToArray())
}
} else {
$Type::$Method.Invoke($Argument)
}
} catch {
Write-Verbose "We don't care about errors. If you do, it was: $_"
}
}
}
New-Alias -Name ^ -Value Invoke-Static -Force
#region tests
$ToSplat = @{
format = "Using 'splatting' hashtable: {0:N2} and {1:N3}"
args = [math]::PI, [math]::E
}
$Array = @(
"Using 'splatting' array: {0:N3}"
[math]::PI
)
^ {
$Dont = @{
Define = 'Anything'
Inside = 'Block'
}
[string]::Format($ToSplat)
[string]::Format($Array)
$This::Silently($Fail)
'Everything else is simply ignored'
Stop-Computer -Force
}
$Replacer = @{
input = 'bartosz bielawski'
pattern = '\b\w'
evaluator = { $args[0].Value.ToUpper() }
}
^ { [regex]::Replace($Replacer) }
$Replacer.evaluator = { $args[0].Value * 5 }
^ { [regex]::Replace($Replacer) }
$Replacer.evaluator = { "[{0}]" -f $args[0].Value.ToUpper() }
^ { [regex]::Replace($Replacer) }
#endregion