Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 5ec67f2

Browse files
authored
[release/3.1] Port 6.0 fix to AssemblyDependencyResolver to not throw for same file (#28161)
Runtime follow up fixes for .NET 3.1 - dotnet/runtime#42055
1 parent ab67f53 commit 5ec67f2

File tree

2 files changed

+86
-1
lines changed

2 files changed

+86
-1
lines changed

src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,9 @@ public AssemblyDependencyResolver(string componentAssemblyPath)
9999
_assemblyPaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
100100
foreach (string assemblyPath in assemblyPaths)
101101
{
102-
_assemblyPaths.Add(Path.GetFileNameWithoutExtension(assemblyPath), assemblyPath);
102+
// Add the first entry with the same simple assembly name if there are multiples
103+
// and ignore others
104+
_assemblyPaths.TryAdd(Path.GetFileNameWithoutExtension(assemblyPath), assemblyPath);
103105
}
104106

105107
_nativeSearchPaths = SplitPathsList(nativeSearchPathsList);

tests/src/Loader/AssemblyDependencyResolver/AssemblyDependencyResolverTests/AssemblyDependencyResolverTests.cs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.IO;
66
using System.Reflection;
7+
using System.Runtime.InteropServices;
78
using System.Runtime.Loader;
89
using TestLibrary;
910
using Xunit;
@@ -130,6 +131,88 @@ public void TestAssembly()
130131
}
131132
}
132133

134+
public void TestAssemblyWithCaseDifferent()
135+
{
136+
// Testing case sensitive file name resolution
137+
// Host policy returns 2 file paths with the casing changed,
138+
// AssemblyDependencyResolver should not throw since the first path exists in the file system
139+
string assemblyDependencyPath = CreateMockAssembly("TestAssemblyWithCaseDifferent.dll");
140+
string nameWOExtension = Path.GetFileNameWithoutExtension(assemblyDependencyPath);
141+
string nameWOExtensionCaseChanged = (Char.IsUpper(nameWOExtension[0]) ? nameWOExtension[0].ToString().ToLower() : nameWOExtension[0].ToString().ToUpper()) + nameWOExtension.Substring(1);
142+
string changeFile = Path.Combine(Path.GetDirectoryName(assemblyDependencyPath), (nameWOExtensionCaseChanged + Path.GetExtension(assemblyDependencyPath)));
143+
144+
IntPtr previousWriter = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(
145+
(HostPolicyMock.ErrorWriterDelegate)((string _) => { Assert.True(false, "Should never get here"); }));
146+
147+
using (HostPolicyMock.MockValues_corehost_set_error_writer errorWriterMock =
148+
HostPolicyMock.Mock_corehost_set_error_writer(previousWriter))
149+
{
150+
using (HostPolicyMock.Mock_corehost_resolve_component_dependencies(
151+
0,
152+
$"{assemblyDependencyPath}{Path.PathSeparator}{changeFile}",
153+
"",
154+
""))
155+
{
156+
AssemblyDependencyResolver resolver = new AssemblyDependencyResolver(changeFile);
157+
158+
string asmResolveName = resolver.ResolveAssemblyToPath(new AssemblyName(nameWOExtensionCaseChanged));
159+
160+
Assert.Equal(
161+
changeFile, asmResolveName, StringComparer.InvariantCultureIgnoreCase
162+
);
163+
164+
// After everything is done, the error writer should be reset to the original value.
165+
Assert.Equal(previousWriter, errorWriterMock.LastSetErrorWriterPtr);
166+
}
167+
}
168+
}
169+
170+
public void TestAssemblyWithCaseReversed()
171+
{
172+
// Testing case sensitive file name resolution
173+
// Host policy returns 2 file paths with the casing changed and names swapped.
174+
// AssemblyDependencyResolver should not throw but has different returned values,
175+
// Based on case sensitive nature of the file system since AssemblyDependencyResolver checks if file exists
176+
// Case insensitive file systems: a valid path is returned
177+
// Case sensitive file systems: null (since the first path does not exist in the system)
178+
string assemblyDependencyPath = CreateMockAssembly("TestAssemblyWithCaseReversed.dll");
179+
string nameWOExtension = Path.GetFileNameWithoutExtension(assemblyDependencyPath);
180+
string nameWOExtensionCaseChanged = (Char.IsUpper(nameWOExtension[0]) ? nameWOExtension[0].ToString().ToLower() : nameWOExtension[0].ToString().ToUpper()) + nameWOExtension.Substring(1);
181+
string changeFile = Path.Combine(Path.GetDirectoryName(assemblyDependencyPath), (nameWOExtensionCaseChanged + Path.GetExtension(assemblyDependencyPath)));
182+
183+
184+
IntPtr previousWriter = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(
185+
(HostPolicyMock.ErrorWriterDelegate)((string _) => { Assert.True(false, "Should never get here"); }));
186+
187+
using (HostPolicyMock.MockValues_corehost_set_error_writer errorWriterMock =
188+
HostPolicyMock.Mock_corehost_set_error_writer(previousWriter))
189+
{
190+
using (HostPolicyMock.Mock_corehost_resolve_component_dependencies(
191+
0,
192+
$"{changeFile}{Path.PathSeparator}{assemblyDependencyPath}",
193+
"",
194+
""))
195+
{
196+
AssemblyDependencyResolver resolver = new AssemblyDependencyResolver(changeFile);
197+
198+
string asmResolveName = resolver.ResolveAssemblyToPath(new AssemblyName(nameWOExtensionCaseChanged));
199+
200+
// Case sensitive systems return null (see notes above)
201+
// We don't check the OS or the file system here since AssemblyDependencyResolver itself stays away from OS specific checks
202+
// In path resolutions
203+
if(asmResolveName != null)
204+
{
205+
Assert.Equal(
206+
assemblyDependencyPath, asmResolveName, StringComparer.InvariantCultureIgnoreCase
207+
);
208+
}
209+
210+
// After everything is done, the error writer should be reset to the original value.
211+
Assert.Equal(previousWriter, errorWriterMock.LastSetErrorWriterPtr);
212+
}
213+
}
214+
}
215+
133216
public void TestAssemblyWithNoRecord()
134217
{
135218
// If the reqest is for assembly which is not listed in .deps.json

0 commit comments

Comments
 (0)