Created
April 17, 2017 02:07
-
-
Save SeeminglyScience/3372a8ef1ac8324f12bb95f14d151580 to your computer and use it in GitHub Desktop.
Proof of concept for forcing external type definitions to export in a module.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using namespace System.Management.Automation.Language | |
using namespace System.Collections.Generic | |
using namespace System.Reflection | |
# The current contents of the psm1 file would go here, including dot sourcing the class definition | |
# files normally and exporting module members. | |
# The rest can most likely be loaded into a function but I haven't tested it yet. It could also | |
# use some cleaning up. | |
$usingStatements = [List[UsingStatementAst]]::new() | |
$text = Get-ChildItem $PSScriptRoot\Classes\*.ps1 | ForEach-Object { | |
# Pull out using statements and type definitions | |
$ast = [Parser]::ParseInput((Get-Content $PSItem.FullName -Raw), [ref]$null, [ref]$null) | |
$ast.UsingStatements.ForEach{ $usingStatements.Add($PSItem) } | |
$ast.FindAll({ $args[0] -is [TypeDefinitionAst] }, $true).Extent.Text | |
} | |
# Join each file along with unique using statements and create a dynamic module. | |
$text = (@($usingStatements.Extent.Text | Sort-Object -Unique) + $text) -join [Environment]::NewLine | |
$module = New-Module -ScriptBlock ([scriptblock]::Create($text)) | |
# This might not be neccessary, it seems the session state is mainly determined by the keeper. | |
$module.SessionState = $ExecutionContext.SessionState | |
# Grab the current SessionStateInternal. | |
$internal = $ExecutionContext.SessionState.GetType(). | |
GetProperty('Internal', [BindingFlags]'Instance, NonPublic'). | |
GetValue($ExecutionContext.SessionState) | |
# Each dynamic type has a non public type called ClassName_<staticHelper>. This contains | |
# wrappers for static methods and a SessionStateKeeper. | |
$module.ImplementingAssembly.DefinedTypes | | |
Where-Object IsPublic -eq $false | | |
ForEach-Object { | |
# Get the session state mapping from the keeper. | |
$keeper = $PSItem. | |
GetField('__sessionStateKeeper', [BindingFlags]'Static, NonPublic'). | |
GetValue($PSItem) | |
$map = $keeper.GetType(). | |
GetField('_stateMap', [BindingFlags]'Instance, NonPublic'). | |
GetValue($keeper) | |
# Remove the current mapping for this runspace and add one with the correct session. | |
$map.Remove($host.Runspace) | |
$map.Add($host.Runspace, $internal) | |
} | |
# Import the dynamic module so it's loaded as a nested module. Exported types from nested modules | |
# aren't cached, so when the using module statement is parsed it will directly load the types from | |
# our nested module. | |
Import-Module $module |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment