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