Skip to content

Commit c6b069e

Browse files
committed
Improve lookup of trusted framework assemblies to use actual path in Assembly.LoadFrom.
1 parent f278bb2 commit c6b069e

File tree

1 file changed

+114
-62
lines changed

1 file changed

+114
-62
lines changed

Clojure/Clojure/Lib/RT.cs

Lines changed: 114 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2758,19 +2758,37 @@ public static string PrintToConsole(object x)
27582758

27592759
#region Locating types
27602760

2761+
2762+
class CaseInsensitiveAssemblyNameComparer : IEqualityComparer<AssemblyName>
2763+
{
2764+
public bool Equals(AssemblyName x, AssemblyName y)
2765+
{
2766+
return string.Equals(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
2767+
}
2768+
2769+
public int GetHashCode(AssemblyName obj)
2770+
{
2771+
return obj.Name.ToLowerInvariant().GetHashCode();
2772+
}
2773+
}
2774+
2775+
27612776
// Cache for all runtime library assembly names, loaded once on demand.
2762-
private static readonly Lazy<List<AssemblyName>> _runtimeAssemblyNames = new(() =>
2777+
// Key is the assembly name. Value is either the path or empty string.
2778+
// If non-empty can use Assembly.LoadFrom(path) to load.
2779+
// If empty, must use Assembly.Load(name).
2780+
2781+
2782+
private static readonly Lazy<Dictionary<AssemblyName, string>> _runtimeAssemblyNames = new(() =>
27632783
{
2764-
var names = new List<AssemblyName>();
2784+
Dictionary<AssemblyName, string> names = new(new CaseInsensitiveAssemblyNameComparer());
27652785

27662786
try
27672787
{
27682788
// DependencyContext.Default can be null in some scenarios (like unit tests or static initializers).
27692789
// Loading the context from a known assembly is more robust.
2770-
27712790
var entryAssembly = Assembly.GetEntryAssembly() ?? typeof(RT).Assembly;
27722791
var context = Microsoft.Extensions.DependencyModel.DependencyContext.Load(entryAssembly);
2773-
27742792
if (context != null)
27752793
{
27762794
foreach (var lib in context.RuntimeLibraries)
@@ -2780,7 +2798,7 @@ public static string PrintToConsole(object x)
27802798
try
27812799
{
27822800
var name = new AssemblyName(Path.GetFileNameWithoutExtension(assembly));
2783-
names.Add(name);
2801+
names[name] = string.Empty;
27842802
}
27852803
catch { }
27862804
}
@@ -2789,49 +2807,103 @@ public static string PrintToConsole(object x)
27892807
}
27902808
catch { }
27912809

2792-
// Also include shared runtime libraries from TRUSTED_PLATFORM_ASSEMBLIES
2793-
try
2794-
{
2810+
27952811
#if (NET48_OR_GREATER || !NETFRAMEWORK)
2796-
var trustedAssemblies = AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES") as string;
2812+
var trustedAssemblies = AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES") as string;
27972813
#else
27982814
var trustedAssemblies = "";
27992815
#endif
2800-
if (!string.IsNullOrEmpty(trustedAssemblies))
2816+
if (!string.IsNullOrEmpty(trustedAssemblies))
2817+
{
2818+
var paths = trustedAssemblies.Split(Path.PathSeparator);
2819+
2820+
foreach (var path in paths)
28012821
{
2802-
var paths = trustedAssemblies.Split(Path.PathSeparator);
2803-
foreach (var path in paths)
2804-
{
2805-
try
2806-
{
2807-
var name = AssemblyName.GetAssemblyName(path);
2808-
if (!names.Any(n => n.Name == name.Name))
2809-
{
2810-
names.Add(name);
2811-
}
2812-
}
2813-
catch { }
2814-
}
2822+
names[new AssemblyName(Path.GetFileNameWithoutExtension(path))] = path;
28152823
}
28162824
}
2817-
catch { }
28182825

28192826
return names;
28202827
});
28212828

2829+
2830+
2831+
// // Cache for all runtime library assembly names, loaded once on demand.
2832+
// private static readonly Lazy<List<AssemblyName>> _runtimeAssemblyNames = new(() =>
2833+
// {
2834+
// var names = new List<AssemblyName>();
2835+
2836+
// try
2837+
// {
2838+
// // DependencyContext.Default can be null in some scenarios (like unit tests or static initializers).
2839+
// // Loading the context from a known assembly is more robust.
2840+
2841+
// var entryAssembly = Assembly.GetEntryAssembly() ?? typeof(RT).Assembly;
2842+
// var context = Microsoft.Extensions.DependencyModel.DependencyContext.Load(entryAssembly);
2843+
2844+
// if (context != null)
2845+
// {
2846+
// foreach (var lib in context.RuntimeLibraries)
2847+
// {
2848+
// foreach (var assembly in lib.RuntimeAssemblyGroups.SelectMany(g => g.AssetPaths))
2849+
// {
2850+
// try
2851+
// {
2852+
// var name = new AssemblyName(Path.GetFileNameWithoutExtension(assembly));
2853+
// names.Add(name);
2854+
// }
2855+
// catch { }
2856+
// }
2857+
// }
2858+
// }
2859+
// }
2860+
// catch { }
2861+
2862+
// // Also include shared runtime libraries from TRUSTED_PLATFORM_ASSEMBLIES
2863+
// try
2864+
// {
2865+
//#if (NET48_OR_GREATER || !NETFRAMEWORK)
2866+
// var trustedAssemblies = AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES") as string;
2867+
//#else
2868+
// var trustedAssemblies = "";
2869+
//#endif
2870+
// if (!string.IsNullOrEmpty(trustedAssemblies))
2871+
// {
2872+
// var paths = trustedAssemblies.Split(Path.PathSeparator);
2873+
// foreach (var path in paths)
2874+
// {
2875+
// try
2876+
// {
2877+
// var name = AssemblyName.GetAssemblyName(path);
2878+
// if (!names.Any(n => n.Name == name.Name))
2879+
// {
2880+
// names.Add(name);
2881+
// }
2882+
// }
2883+
// catch { }
2884+
// }
2885+
// }
2886+
// }
2887+
// catch { }
2888+
2889+
// return names;
2890+
// });
2891+
28222892
internal static readonly char[] _triggerTypeChars = ['`', ',', '[', '&', '*'];
28232893

28242894
public static Type classForName(string p)
28252895
{
28262896
return classForName(p, CurrentNSVar.deref() as Namespace, true);
28272897
}
28282898

2899+
// Leaving in this metering code for now.
2900+
// Remove after we're done debugging.
2901+
28292902
public static int NumDuplicates { get; set; } = 0;
28302903
public static int NumDirects { get; set; } = 0;
28312904
public static int NumLoadedAssemblyFinds { get; set; } = 0;
28322905
public static int NumRuntimeAssemblyLoads { get; set; } = 0;
28332906
public static int NumRuntimeAssemblyFinds { get; set; } = 0;
2834-
public static int NumLoadedAssemblyFinds2 { get; set; } = 0;
28352907
public static int NumParsing { get; set; } = 0;
28362908
public static int NumFails { get; set; } = 0;
28372909

@@ -2842,10 +2914,8 @@ public static void ClassForNameReport()
28422914
Console.WriteLine($"NumLoadedAssemblyFinds: {NumLoadedAssemblyFinds}");
28432915
Console.WriteLine($"NumRuntimeAssemblyLoads: {NumRuntimeAssemblyLoads}");
28442916
Console.WriteLine($"NumRuntimeAssemblyFinds: {NumRuntimeAssemblyFinds}");
2845-
Console.WriteLine($"NumLoadedAssemblyFinds2: {NumLoadedAssemblyFinds2}");
28462917
Console.WriteLine($"NumParsing: {NumParsing}");
28472918
Console.WriteLine($"NumFails: {NumFails}");
2848-
28492919
}
28502920

28512921
internal static Type classForName(string p, Namespace ns, bool canCallClrTypeSpec)
@@ -2896,53 +2966,35 @@ internal static Type classForName(string p, Namespace ns, bool canCallClrTypeSpe
28962966

28972967
if (!string.IsNullOrEmpty(namespaceName))
28982968
{
2899-
29002969
var runtimeAssemblyNames = _runtimeAssemblyNames.Value;
2901-
if (runtimeAssemblyNames.Count > 0)
2970+
2971+
var targetAssemblyName = new AssemblyName(namespaceName);
2972+
2973+
// try if the namespace directly matches the assembly name.
2974+
if (runtimeAssemblyNames.TryGetValue(targetAssemblyName, out var path))
29022975
{
29032976

2904-
// Try to find and load the assembly that might contain this type.
2905-
foreach (var assemblyName in runtimeAssemblyNames)
2977+
if (loadedAssemblies.Any(a => a.GetName().Name.Equals(namespaceName, StringComparison.OrdinalIgnoreCase)))
29062978
{
29072979
// Skip if this assembly is already loaded.
2908-
if (loadedAssemblies.Any(a => a.GetName().Name == assemblyName.Name))
2909-
{
2910-
continue;
2911-
}
2912-
2913-
// try if the namespace directly matches the assembly name.
2914-
if (assemblyName.Name.Equals(namespaceName, StringComparison.OrdinalIgnoreCase))
2980+
}
2981+
else
2982+
{
2983+
NumRuntimeAssemblyLoads++;
2984+
var assy = string.IsNullOrWhiteSpace(path) ? Assembly.Load(targetAssemblyName) : Assembly.LoadFrom(path);
2985+
var type = assy.GetType(p, false);
2986+
if (type != null && (type.IsPublic || type.IsNestedPublic))
29152987
{
2916-
try
2917-
{
2918-
NumRuntimeAssemblyLoads++;
2919-
var assy = Assembly.Load(assemblyName);
2920-
var type = assy.GetType(p, false);
2921-
if (type != null && (type.IsPublic || type.IsNestedPublic))
2922-
{
2923-
NumRuntimeAssemblyFinds++;
2924-
return type;
2925-
}
2926-
}
2927-
catch { }
2988+
NumRuntimeAssemblyFinds++;
2989+
return type;
29282990
}
2929-
29302991
}
29312992
}
29322993
}
29332994
}
29342995

2935-
//// Re-check loaded assemblies (some might have been loaded during the search).
2936-
//loadedAssemblies = domain.GetAssemblies();
2937-
//foreach (Assembly assy in loadedAssemblies)
2938-
//{
2939-
// Type t1 = assy.GetType(p, false);
2940-
// if (t1 != null && (t1.IsPublic || t1.IsNestedPublic))
2941-
// {
2942-
// NumLoadedAssemblyFinds2++;
2943-
// return t1;
2944-
// }
2945-
//}
2996+
2997+
29462998

29472999
// Search by simple type name (slow path).
29483000
// At some point this became a no-op unless running on Mono, so I restricted it to that.

0 commit comments

Comments
 (0)