Skip to content

Instantly share code, notes, and snippets.

@Jaykul
Last active March 12, 2018 23:34
Show Gist options
  • Save Jaykul/b41f9e30a76353467718 to your computer and use it in GitHub Desktop.
Save Jaykul/b41f9e30a76353467718 to your computer and use it in GitHub Desktop.
Types And Inheritance
Import-Module poke -Force
$xlr8r = [psobject].assembly.gettype("System.Management.Automation.TypeAccelerators")
# ScriptBlock Parsing using a Parser Instance so we can pass it to the DefineTypeHelper later
function New-Type {
param(
[ScriptBlock]$TypeDefinition,
[System.Reflection.AssemblyName]$AssemblyName="PowerShell",
[Version]$Version = $AssemblyName.Version,
[string]$Namespace,
[Type]$BaseType,
[Type[]]$Interfaces,
[String]$Path = $Pwd,
[Alias("Access")]
[System.Reflection.Emit.AssemblyBuilderAccess]$AssemblyBuilderAccess = "Run",
[Switch]$Passthru
)
$Parser = (New-TypeProxy 'System.Management.Automation.Language.Parser').__CreateInstance()
$ParseErrors = [System.Management.Automation.Language.ParseError[]]@()
$Tokens = [Collections.Generic.List[Management.Automation.Language.Token]]::new()
$scriptBlockAst = $Parser.Parse($null, "$TypeDefinition", $Tokens, $ParseErrors)
# We always want a version
$AssemblyName.Version = if($PSBoundParameters.ContainsKey("Version")) {
$Version
} elseif(!$AssemblyName.Version) {
"1.0.0"
}
$TypeDefiner = New-TypeProxy "System.Management.Automation.Language.TypeDefiner"
$AssemblyCustomAttributes = [System.Collections.Generic.List[System.Reflection.Emit.CustomAttributeBuilder]]$TypeDefiner.GetAssemblyAttributeBuilders()
$AssemblyBuilder = [System.AppDomain]::CurrentDomain.DefineDynamicAssembly( $AssemblyName, $AssemblyBuilderAccess, $Path, $True, $AssemblyCustomAttributes )
if($AssemblyBuilderAccess -bAnd "Save") {
$ModuleBuilder = $assemblyBuilder.DefineDynamicModule($($AssemblyBuilder.GetName().Name), "${AssemblyName}.dll")
} else {
$ModuleBuilder = $assemblyBuilder.DefineDynamicModule($($AssemblyBuilder.GetName().Name))
}
# We need a dummy ModuleBuilder (in a dummy AssemblyBuilder) to initialize the defineTypeHelper
# DefineTypeHelper defines the type in the constructor, but we need to call an alternate DefineType
$DummyBuilder = [System.AppDomain]::CurrentDomain.DefineDynamicAssembly( $AssemblyName, "Run")
$DummyModule = $DummyBuilder.DefineDynamicModule("Dummy")
# $e_scriptblockAst.ImplementingAssembly = $AssemblyBuilder
$typeDefinitionAst = $scriptBlockAst.Find({param($ast) Where-Object -Input $ast -FilterScript { $_ -is [System.Management.Automation.Language.TypeDefinitionAst] } }, $False)
$ClassName = $typeDefinitionAst.Name
$StaticName = "<{0}_staticHelpers>" -f $typeDefinitionAst.Name
if($Namespace) {
$ClassName = "${Namespace}.${ClassName}"
$StaticName = "${Namespace}.${StaticName}"
}
$defineTypeHelper = (New-TypeProxy 'System.Management.Automation.Language.TypeDefiner+DefineTypeHelper').__CreateInstance(
[System.Management.Automation.Language.Parser]$Parser.__GetBaseObject(),
[System.Reflection.Emit.ModuleBuilder]$DummyModule,
[System.Management.Automation.Language.TypeDefinitionAst]$typeDefinitionAst,
[string]"${ClassName}"
)
#
# Hacking DefineTypeHelper:
# Replace the dummy module builder, and call DefineType with support for BaseType and Interfaces
$defineTypeHelper._ModuleBuilder = $ModuleBuilder
$defineTypeHelper._typeBuilder = $TypeBuilder =
if ($BaseType -and $Interfaces) {
$ModuleBuilder.DefineType("${ClassName}", 'Public', [type]$BaseType, [type[]]$Interfaces)
} elseif ( $BaseType) {
$ModuleBuilder.DefineType("${ClassName}", 'Public', [type]$BaseType)
} else {
$ModuleBuilder.DefineType("${ClassName}", 'Public')
}
# $Namespace = $FullName.SubString(0, $FullName.LastIndexOf("."))
$defineTypeHelper._staticHelpersTypeBuilder = $TypeStaticBuilder = $ModuleBuilder.DefineType($StaticName, [System.Reflection.TypeAttributes]"NotPublic")
[void] $defineTypeHelper.DefineMembers()
$Type = $TypeBuilder.CreateTypeInfo()
(New-ObjectProxy $typeDefinitionAst).Type = $Type
$null = $xlr8r::Remove( ${ClassName} )
$xlr8r::Add(${ClassName}, $Type)
if($Passthru) {
Write-Output $Type
}
$TypeStatic = $TypeStaticBuilder.CreateTypeInfo()
if ($defineTypeHelper._fieldsToInitForMemberFunctions -ne $null) {
foreach ($tuple in $defineTypeHelper._fieldsToInitForMemberFunctions) {
$Field = $TypeStaticBuilder.GetField([string]$tuple.Item1,[System.Reflection.BindingFlags]"Static,NonPublic")
$Field.SetValue([System.Management.Automation.Internal.AutomationNull]::Value, [System.Management.Automation.ScriptBlockMemberMethodWrapper]$tuple.Item2)
}
}
[void] $TypeBuilder.CreateType()
[void] $TypeStaticBuilder.CreateType()
if($AssemblyBuilderAccess -bAnd "Save") {
[void] $AssemblyBuilder.Save("${AssemblyName}.dll")
}
}
$Type = New-Type {
class MyClassTest {
[string] $name = 'NameValue'
hidden [string] $hiddenName = 'HiddenNameValue'
static [string] $staticName = 'StaticNameValue'
hidden static [string] $hiddenStaticName = 'HiddenStaticNameValue'
MyClassTest() {
Write-Host "Hello World" -Foreground Red
}
[string] Get($Name) {
$this.Name = $Name
return $this.Name
}
hidden [string] HiddenGet() {
return [MyClassTest]::staticName
}
static [string] StaticGet([hashtable]$hash,[string]$titi) {
return [MyClassTest]::staticName
}
static [string] StaticGet([hashtable]$hash) {
return [MyClassTest]::staticName
}
hidden static [string] HiddenStaticGet() {
return [MyClassTest]::staticName
}
[System.Collections.IEnumerator] GetEnumerator() {
return [MyClassTest]::staticName.GetEnumerator()
}
}
} -BaseType System.Linq.Expressions.DynamicExpressionVisitor -Interfaces System.Collections.IEnumerable -Access RunAndSave
$Type::staticName
$Type.FullName
$Type::New
$MyThing = $Type::New()
$MyThing.Visit # show that the inherited members exist ;-)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment