# encoding: ascii
# api: powershell
# title: Get-Delegate.ps1
# description: Use Reflection and IL to emit arbitrary delegatesfrom PowerShell script
# version: 0.1
# type: script
# author: Joel Bennett
# license: CC0
# x-poshcode-id: 194
# x-archived: 2014-01-03T12:10:51
# x-published: 2009-05-06T13:00:00
#
#
## Get-Delegate.ps1
###################################################################################################
## Use Reflection and IL to emit arbitrary delegates ...
## A trick they taught us on the PowerShell blog
## http://blogs.msdn.com/powershell/archive/2006/07/25/678259.aspx
###################################################################################################
param([type] $type, [ScriptBlock] $scriptBlock)
# Helper function to emit an IL opcode
function emit($opcode)
{
if ( ! ($op = [System.Reflection.Emit.OpCodes]::($opcode)))
{
throw "new-method: opcode '$opcode' is undefined"
}
if ($args.Length -gt 0)
{
$ilg.Emit($op, $args[0])
}
else
{
$ilg.Emit($op)
}
}
# Get the method info for this delegate invoke...
$delegateInvoke = $type.GetMethod("Invoke")
# Get the argument type signature for the delegate invoke
$parameters = @($delegateInvoke.GetParameters())
$returnType = $delegateInvoke.ReturnParameter.ParameterType
$argList = new-object Collections.ArrayList
[void] $argList.Add([ScriptBlock])
foreach ($p in $parameters)
{
[void] $argList.Add($p.ParameterType);
}
$dynMethod = new-object reflection.emit.dynamicmethod ("",
$returnType, $argList.ToArray(), [object], $false)
$ilg = $dynMethod.GetILGenerator()
# Place the scriptblock on the stack for the method call
emit Ldarg_0
emit Ldc_I4 ($argList.Count - 1) # Create the parameter array
emit Newarr ([object])
for ($opCount = 1; $opCount -lt $argList.Count; $opCount++)
{
emit Dup # Dup the array reference
emit Ldc_I4 ($opCount - 1); # Load the index
emit Ldarg $opCount # Load the argument
if ($argList[$opCount].IsValueType) # Box if necessary
{
emit Box $argList[$opCount]
}
emit Stelem ([object]) # Store it in the array
}
# Now emit the call to the ScriptBlock invoke method
emit Call ([ScriptBlock].GetMethod("InvokeReturnAsIs"))
if ($returnType -eq [void])
{
# If the return type is void, pop the returned object
emit Pop
}
else
{
# Otherwise emit code to convert the result type which looks
# like LanguagePrimitives.ConvertTo(value, type)
$signature = [object], [type]
$convertMethod =
[Management.Automation.LanguagePrimitives].GetMethod(
"ConvertTo", $signature);
$GetTypeFromHandle = [Type].GetMethod("GetTypeFromHandle");
emit Ldtoken $returnType # And the return type token...
emit Call $GetTypeFromHandle
emit Call $convertMethod
}
emit Ret
#
# Now return a delegate from this dynamic method...
#
$dynMethod.CreateDelegate($type, $scriptBlock)