Legislator.psm1
using namespace System.Collections.Generic using namespace System.Reflection using namespace System.Reflection.Emit function event { param( [Parameter(Mandatory = $true, Position = 0)] [string]$TypeName, [Parameter(Mandatory = $true, Position = 1)] [Alias('Name')] [string]$EventName, [Parameter(Mandatory = $false, Position = 2)] [string]$Option ) Assert-Legislator -MemberType event try{ $handlerType = [Type]$TypeName } catch { throw [Exception]::new('Unrecognized type name', $_) return } $eventBuilder = $Legislator.DefineEvent($EventName, [EventAttributes]::None, $handlerType); $eventMethodAttributes = @( 'Public', 'HideBySig', 'SpecialName', 'Abstract', 'Virtual', 'NewSlot' ) -as [MethodAttributes] $addMethod = . method -TypeName:'void' -Name:"add_$EventName" -Attributes:$eventMethodAttributes -ParameterTypes @( $HandlerType ) -PassThru:$true $addMethod.DefineParameter(1, [ParameterAttributes]::None, 'value') $eventBuilder.SetAddOnMethod($addMethod) $removeMethod = . method -TypeName:'void' -Name:"remove_$EventName" -Attributes:$eventMethodAttributes -ParameterTypes @( $HandlerType ) -PassThru:$true $removeMethod.DefineParameter(1, [ParameterAttributes]::None, 'value') $eventBuilder.SetRemoveOnMethod($removeMethod) } function interface { param( [Parameter(Mandatory = $true, Position = 0)] [ValidatePattern('^[\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}][\p{Lu}\p{Ll}\p{Lt}\p{Lm}\p{Lo}\p{Nl}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\p{Cf}]*$')] [string]$Name, [Parameter(Mandatory = $true, Position = 1)] [ValidateScript({ -not ( @($_.Ast.FindAll({ param($AST) $AST -is [System.Management.Automation.Language.CommandAst] },$true) |ForEach-Object GetCommandName) |Where-Object { $_ -notin 'property','method','event' } ) -or $(throw 'Only properties and methods can be defined in an interface') })] [scriptblock]$Definition, [Parameter()] [ValidateScript({-not($_ |Where-Object{-not $_.IsInterface})})] [type[]]$Implements, [Parameter()] [switch]$PassThru = $false ) if($Name -cnotlike 'I*'){ Write-Warning -Message "Naming rule violaion: Missing prefix: 'I'" } $interfaceAttributes = @( 'Public','Interface','Abstract','AnsiClass','AutoLayout' ) -as [TypeAttributes] $runtimeGuid = $((New-Guid)-replace'\W') $assemblyName = [AssemblyName]::new("$Name$runtimeGuid") $assemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($assemblyName, [AssemblyBuilderAccess]::Run) $moduleBuilder = $assemblyBuilder.DefineDynamicModule("__psinterfacemodule_$assemblyName") $Legislator = $moduleBuilder.DefineType($Name, $interfaceAttributes) if($PSBoundParameters.ContainsKey('Implements')){ foreach($interfaceImpl in $Implements |Sort-Object -Property FullName -Unique){ try{ $Legislator.AddInterfaceImplementation($interfaceImpl) } catch{ throw return } } } $null = . $Definition if($?){ $finalType = $Legislator.CreateType() if($PassThru){ return $finalType } } } function Assert-Legislator { param ( [string]$MemberType ) if (-not $Legislator) { throw "$MemberType only allowed in interface declarations" } } function method { param( [Parameter(Mandatory = $true, Position = 0)] [string]$TypeName, [Parameter(Mandatory = $true, Position = 1)] [Alias('Name')] [string]$MethodName, [Parameter(Mandatory = $false, Position = 2)] [AllowEmptyCollection()] [Type[]]$ParameterTypes, [Parameter(DontShow)] [MethodAttributes]$Attributes, [Parameter(DontShow)] [switch]$PassThru ) Assert-Legislator -MemberType method try{ $ReturnType = [Type]$TypeName } catch { throw [Exception]::new('Unrecognized type name', $_) return } $interfaceMethodAttributes = @( 'Public', 'HideBySig', 'Abstract', 'Virtual', 'NewSlot' ) -as [MethodAttributes] $interfaceMethodAttributes = $interfaceMethodAttributes -bor $Attributes $method = $Legislator.DefineMethod($MethodName, $interfaceMethodAttributes, $ReturnType, $ParameterTypes) if($PassThru){ return $method } } function property { param( [Parameter(Mandatory = $true, Position = 0)] [string]$TypeName, [Parameter(Mandatory = $true, Position = 1)] [Alias('Name')] [string]$PropertyName, [Parameter(Mandatory = $false, Position = 2)] [ValidateSet('ReadOnly')] [string]$Option ) Assert-Legislator -MemberType property try{ $Type = [Type]$TypeName } catch { throw [Exception]::new('Unrecognized type name', $_) return } $property = $Legislator.DefineProperty($PropertyName, [PropertyAttributes]::HasDefault, [CallingConventions]::HasThis, $Type, $null) $propertyMethodAttributes = @( 'Public', 'HideBySig', 'SpecialName', 'Abstract', 'Virtual', 'NewSlot' ) -as [MethodAttributes] $getMethod = . method -TypeName:$Type.FullName -Name:"get_$PropertyName" -Attributes:$propertyMethodAttributes -PassThru:$true $property.SetGetMethod($getMethod) if($Option -ne 'ReadOnly') { $setMethod = . method -TypeName:"void" -Name:"set_$PropertyName" -Attributes:$propertyMethodAttributes -ParameterTypes @( $Type ) -PassThru:$true $null = $setMethod.DefineParameter(1, [ParameterAttributes]::None, 'value'); $property.SetSetMethod($setMethod) } } |