Discover assemblies loaded by 'Assembly.Load(byte[])' and 'Assembly.LoadFile' (#12203)

* Fix regression: pwsh should discover assemblies loaded by 'Assembly.Load(byte[])' and 'Assembly.LoadFile'
This commit is contained in:
Dongbo Wang 2020-03-28 08:25:59 -07:00 committed by GitHub
parent dfe9955346
commit e741dc58dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 78 additions and 3 deletions

1
.gitignore vendored
View file

@ -1,5 +1,6 @@
bin/
obj/
.ionide/
project.lock.json
*-tests.xml
/debug/

View file

@ -58,9 +58,36 @@ namespace System.Management.Automation
/// </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>

View file

@ -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"
}
}