Skip to content

Commit

Permalink
Discover assemblies loaded by 'Assembly.Load(byte[])' and 'Assembly.L…
Browse files Browse the repository at this point in the history
…oadFile' (PowerShell#12203)

* Fix regression: pwsh should discover assemblies loaded by 'Assembly.Load(byte[])' and 'Assembly.LoadFile'
  • Loading branch information
daxian-dbw committed Mar 28, 2020
1 parent dfe9955 commit e741dc5
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
bin/
obj/
.ionide/
project.lock.json
*-tests.xml
/debug/
Expand Down
33 changes: 30 additions & 3 deletions src/System.Management.Automation/utils/ClrFacade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,36 @@ internal static IEnumerable<Assembly> GetAssemblies(TypeResolutionState typeReso
/// </param>
internal static IEnumerable<Assembly> GetAssemblies(string namespaceQualifiedTypeName = null)
{
return PSAssemblyLoadContext.GetAssembly(namespaceQualifiedTypeName) ??
AssemblyLoadContext.Default.Assemblies.Where(a =>
!a.FullName.StartsWith(TypeDefiner.DynamicClassAssemblyFullNamePrefix, StringComparison.Ordinal));
return PSAssemblyLoadContext.GetAssembly(namespaceQualifiedTypeName) ?? GetPSVisibleAssemblies();
}

/// <summary>
/// Return assemblies from the default load context and the 'individual' load contexts.
/// The 'individual' load contexts are the ones holding assemblies loaded via 'Assembly.Load(byte[])' and 'Assembly.LoadFile'.
/// Assemblies loaded in any custom load contexts are not consider visible to PowerShell to avoid type identity issues.
/// </summary>
private static IEnumerable<Assembly> GetPSVisibleAssemblies()
{
const string IndividualAssemblyLoadContext = "System.Runtime.Loader.IndividualAssemblyLoadContext";

foreach (Assembly assembly in AssemblyLoadContext.Default.Assemblies)
{
if (!assembly.FullName.StartsWith(TypeDefiner.DynamicClassAssemblyFullNamePrefix, StringComparison.Ordinal))
{
yield return assembly;
}
}

foreach (AssemblyLoadContext context in AssemblyLoadContext.All)
{
if (IndividualAssemblyLoadContext.Equals(context.GetType().FullName, StringComparison.Ordinal))
{
foreach (Assembly assembly in context.Assemblies)
{
yield return assembly;
}
}
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

Describe "Assembly loaded in IndividualAssemblyLoadContext should be visible to PowerShell" -Tags "CI" {
BeforeAll {
$code1 = @'
namespace LoadBytes {
public class MyLoadBytesTest {
public static string GetName() { return "MyLoadBytesTest"; }
}
}
'@
$code2 = @'
namespace LoadFile {
public class MyLoadFileTest {
public static string GetName() { return "MyLoadFileTest"; }
}
}
'@

$tempFolderPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "IndividualALCTest")
New-Item $tempFolderPath -ItemType Directory -Force > $null
$loadBytesFile = [System.IO.Path]::Combine($tempFolderPath, "MyLoadBytesTest.dll")
$loadFileFile = [System.IO.Path]::Combine($tempFolderPath, "MyLoadFileTest.dll")

if (-not (Test-Path $loadBytesFile)) {
Add-Type -TypeDefinition $code1 -OutputAssembly $loadBytesFile
}

if (-not (Test-Path $loadFileFile)) {
Add-Type -TypeDefinition $code2 -OutputAssembly $loadFileFile
}
}

It "Assembly loaded via 'Assembly.Load(byte[])' should be discoverable" {
$bytes = [System.IO.File]::ReadAllBytes($loadBytesFile)
[System.Reflection.Assembly]::Load($bytes) > $null

[LoadBytes.MyLoadBytesTest]::GetName() | Should -BeExactly "MyLoadBytesTest"
}

It "Assembly loaded via 'Assembly.LoadFile' should be discoverable" {
[System.Reflection.Assembly]::LoadFile($loadFileFile) > $null

[LoadFile.MyLoadFileTest]::GetName() | Should -BeExactly "MyLoadFileTest"
}
}

0 comments on commit e741dc5

Please sign in to comment.