Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is that IndividualAssemblyLoadContext is internal class and .Net is free to change the name.
Maybe more reliable to use an assembly context name ("Assembly.Load(byte[], ...)" and "Assembly.LoadFile({0})"). It is public contract and has one prefix Assembly.Load for both.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any one can create a ALC instance with the name "Assembly.Load(byte[], ...)" or "Assembly.LoadFile(".
It's less reliable to depend on the name than the type. They can change the type name, but I think that possibility is very low, and we have test to catch the failure if the type name really changes.


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