2525using System . Text . RegularExpressions ;
2626using System . Threading ;
2727using RTProperties = clojure . runtime . Properties ;
28+ using Microsoft . Extensions . DependencyModel ;
2829
2930
3031namespace clojure . lang
@@ -40,7 +41,19 @@ public static class RT
4041 static Dictionary < Symbol , Type > CreateDefaultImportDictionary ( )
4142 {
4243 var q = GetAllTypesInNamespace ( "System" ) ;
43- var d = q . ToDictionary ( keySelector : t => Symbol . intern ( t . Name ) ) ;
44+ // Handle duplicates by taking the first occurrence of each type name
45+ // This is necessary because in .NET Core/5+ the same type name can appear
46+ // in multiple assemblies (e.g., "Casing" appears in both System.Text.Json
47+ // and System.Private.CoreLib)
48+ var d = new Dictionary < Symbol , Type > ( ) ;
49+ foreach ( var type in q )
50+ {
51+ var symbol = Symbol . intern ( type . Name ) ;
52+ if ( ! d . ContainsKey ( symbol ) )
53+ {
54+ d . Add ( symbol , type ) ;
55+ }
56+ }
4457
4558 // ADDED THESE TO SUPPORT THE BOOTSTRAPPING IN THE JAVA CORE.CLJ
4659 d . Add ( Symbol . intern ( "StringBuilder" ) , typeof ( StringBuilder ) ) ;
@@ -2751,66 +2764,183 @@ public static string PrintToConsole(object x)
27512764
27522765 #region Locating types
27532766
2767+ // Cache for all runtime library assembly names, loaded once on demand.
2768+ private static readonly Lazy < List < AssemblyName > > _runtimeAssemblyNames = new Lazy < List < AssemblyName > > ( ( ) =>
2769+ {
2770+ var names = new List < AssemblyName > ( ) ;
2771+
2772+ try
2773+ {
2774+ // DependencyContext.Default can be null in some scenarios (like unit tests or static initializers).
2775+ // Loading the context from a known assembly is more robust.
2776+ var entryAssembly = Assembly . GetEntryAssembly ( ) ;
2777+
2778+ // If there's no entry assembly (e.g., when hosted in a non-standard way),
2779+ // fall back to the assembly that contains the RT class itself (Clojure.dll).
2780+ if ( entryAssembly == null )
2781+ {
2782+ entryAssembly = typeof ( RT ) . Assembly ;
2783+ }
2784+
2785+ var context = Microsoft . Extensions . DependencyModel . DependencyContext . Load ( entryAssembly ) ;
2786+
2787+ if ( context != null )
2788+ {
2789+ foreach ( var lib in context . RuntimeLibraries )
2790+ {
2791+ foreach ( var assembly in lib . RuntimeAssemblyGroups . SelectMany ( g => g . AssetPaths ) )
2792+ {
2793+ try
2794+ {
2795+ var name = new AssemblyName ( Path . GetFileNameWithoutExtension ( assembly ) ) ;
2796+ names . Add ( name ) ;
2797+ }
2798+ catch { }
2799+ }
2800+ }
2801+ }
2802+ }
2803+ catch { }
2804+
2805+ // Also include shared runtime libraries from TRUSTED_PLATFORM_ASSEMBLIES
2806+ try
2807+ {
2808+ var trustedAssemblies = AppContext . GetData ( "TRUSTED_PLATFORM_ASSEMBLIES" ) as string ;
2809+ if ( ! string . IsNullOrEmpty ( trustedAssemblies ) )
2810+ {
2811+ var paths = trustedAssemblies . Split ( Path . PathSeparator ) ;
2812+ foreach ( var path in paths )
2813+ {
2814+ try
2815+ {
2816+ var name = AssemblyName . GetAssemblyName ( path ) ;
2817+ if ( ! names . Any ( n => n . Name == name . Name ) )
2818+ {
2819+ names . Add ( name ) ;
2820+ }
2821+ }
2822+ catch { }
2823+ }
2824+ }
2825+ }
2826+ catch { }
2827+
2828+ return names ;
2829+ } ) ;
2830+
27542831 static readonly char [ ] _triggerTypeChars = new char [ ] { '`' , ',' , '[' , '&' } ;
27552832
27562833 public static Type classForName ( string p )
27572834 {
2758-
2759- // This used to come later. Moved it up to the top for compiling definterface, e.g.
2760- // (definterface IMyInterface ... )
2761- // First compiled create IMyInterface classs, gets stored in the compiled-types map.
2762- // Then eval'd so the we update the current environment and store the the eval-types map.
2763- // However, definterface does an import, it picks up the version in the eval-types map.
2764- // When a subsequent call tries to get IMyInterface, it was being found by Type.GetType.
2765- // So code being compiled was picking up the eval'd version instead of the compiled version.
2766-
2835+ // First, check for types generated during the current compilation session.
27672836 Type t = Compiler . FindDuplicateType ( p ) ;
27682837 if ( t != null )
2838+ {
27692839 return t ;
2840+ }
27702841
2771- // fastest path, will succeed for assembly qualified names (returned by Type.AssemblyQualifiedName)
2772- // or namespace qualified names (returned by Type.FullName) in the executing assembly or mscorlib
2773- // e.g. "UnityEngine.Transform, UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
2842+ // Try using Type.GetType which can find types with assembly-qualified names.
27742843 t = Type . GetType ( p , false ) ;
2775-
2776- // Added the IsPublic check to deal with shadowed types in .Net Core,
2777- // e.g. System.Environment in assemblies System.Private.CoreLib and System.Runtime.Exceptions.
2778- // It is private in the former and public in the latter.
2779- // Unfortunately, Type.GetType was finding the former.
27802844 if ( t != null && ( t . IsPublic || t . IsNestedPublic ) )
2845+ {
27812846 return t ;
2847+ }
27822848
2849+ // Search through currently loaded assemblies.
2850+ AppDomain domain = AppDomain . CurrentDomain ;
2851+ Assembly [ ] loadedAssemblies = domain . GetAssemblies ( ) ;
2852+
2853+ // Search by namespace-qualified name in loaded assemblies.
2854+ foreach ( Assembly assy in loadedAssemblies )
2855+ {
2856+ Type t1 = assy . GetType ( p , false ) ;
2857+ if ( t1 != null && ( t1 . IsPublic || t1 . IsNestedPublic ) )
2858+ {
2859+ return t1 ;
2860+ }
2861+ }
27832862
2863+ // Try to load from runtime libraries if we have DependencyContext.
2864+ var runtimeAssemblyNames = _runtimeAssemblyNames . Value ;
2865+ if ( runtimeAssemblyNames . Count > 0 )
2866+ {
2867+ // Split the type name to identify the namespace and simple name.
2868+ string namespaceName = null ;
2869+ string typeName = p ;
2870+ int lastDot = p . LastIndexOf ( '.' ) ;
2871+ if ( lastDot > 0 )
2872+ {
2873+ namespaceName = p . Substring ( 0 , lastDot ) ;
2874+ typeName = p . Substring ( lastDot + 1 ) ;
2875+ }
27842876
2877+ // Try to find and load the assembly that might contain this type.
2878+ foreach ( var assemblyName in runtimeAssemblyNames )
2879+ {
2880+ // Skip if this assembly is already loaded.
2881+ if ( loadedAssemblies . Any ( a => a . GetName ( ) . Name == assemblyName . Name ) )
2882+ {
2883+ continue ;
2884+ }
27852885
2786- AppDomain domain = AppDomain . CurrentDomain ;
2787- Assembly [ ] assys = domain . GetAssemblies ( ) ;
2788- List < Type > candidateTypes = new ( ) ;
2886+ // Try common patterns for framework assemblies.
2887+ if ( namespaceName != null )
2888+ {
2889+ // Check if the assembly name matches the namespace pattern.
2890+ if ( assemblyName . Name . StartsWith ( "System" ) && namespaceName . StartsWith ( "System" ) )
2891+ {
2892+ try
2893+ {
2894+ var assy = Assembly . Load ( assemblyName ) ;
2895+ var type = assy . GetType ( p , false ) ;
2896+ if ( type != null && ( type . IsPublic || type . IsNestedPublic ) )
2897+ {
2898+ return type ;
2899+ }
2900+ }
2901+ catch { }
2902+ }
2903+ // Also try if the namespace directly matches the assembly name.
2904+ else if ( assemblyName . Name . Equals ( namespaceName , StringComparison . OrdinalIgnoreCase ) )
2905+ {
2906+ try
2907+ {
2908+ var assy = Assembly . Load ( assemblyName ) ;
2909+ var type = assy . GetType ( p , false ) ;
2910+ if ( type != null && ( type . IsPublic || type . IsNestedPublic ) )
2911+ {
2912+ return type ;
2913+ }
2914+ }
2915+ catch { }
2916+ }
2917+ }
2918+ }
2919+ }
27892920
2790- // fast path, will succeed for namespace qualified names (returned by Type.FullName)
2791- // e.g. "UnityEngine.Transform"
2792- foreach ( Assembly assy in assys )
2921+ // Re-check loaded assemblies (some might have been loaded during the search).
2922+ loadedAssemblies = domain . GetAssemblies ( ) ;
2923+ foreach ( Assembly assy in loadedAssemblies )
27932924 {
27942925 Type t1 = assy . GetType ( p , false ) ;
27952926 if ( t1 != null && ( t1 . IsPublic || t1 . IsNestedPublic ) )
2927+ {
27962928 return t1 ;
2929+ }
27972930 }
27982931
2799- // slow path, will succeed for display names (returned by Type.Name)
2800- // e.g. "Transform"
2801- foreach ( Assembly assy1 in assys )
2932+ // Search by simple type name (slow path).
2933+ List < Type > candidateTypes = new List < Type > ( ) ;
2934+ foreach ( Assembly assy1 in loadedAssemblies )
28022935 {
28032936 Type t1 = null ;
28042937
28052938 if ( IsRunningOnMono )
28062939 {
2807- // I do not know why Assembly.GetType fails to find types in our assemblies in Mono
2808-
28092940 if ( ! assy1 . IsDynamic )
28102941 {
28112942 try
28122943 {
2813-
28142944 foreach ( Type tt in assy1 . GetTypes ( ) )
28152945 {
28162946 if ( tt . Name . Equals ( p ) )
@@ -2827,20 +2957,23 @@ public static Type classForName(string p)
28272957 }
28282958
28292959 if ( t1 != null && ! candidateTypes . Contains ( t1 ) )
2960+ {
28302961 candidateTypes . Add ( t1 ) ;
2962+ }
28312963 }
28322964
2833- if ( candidateTypes . Count == 0 )
2834- t = null ;
2835- else if ( candidateTypes . Count == 1 )
2836- t = candidateTypes [ 0 ] ;
2837- else // multiple, ambiguous
2838- t = null ;
2965+ if ( candidateTypes . Count == 1 )
2966+ {
2967+ return candidateTypes [ 0 ] ;
2968+ }
28392969
2840- if ( t == null && p . IndexOfAny ( _triggerTypeChars ) != - 1 )
2841- t = ClrTypeSpec . GetTypeFromName ( p ) ;
2970+ // Handle generic types and array types.
2971+ if ( p . IndexOfAny ( _triggerTypeChars ) != - 1 )
2972+ {
2973+ return ClrTypeSpec . GetTypeFromName ( p ) ;
2974+ }
28422975
2843- return t ;
2976+ return null ;
28442977 }
28452978
28462979
0 commit comments