33
44using  System . Diagnostics ; 
55using  System . Diagnostics . CodeAnalysis ; 
6+ using  System . Globalization ; 
67using  System . IO ; 
78using  System . Reflection . Metadata ; 
89using  System . Runtime . CompilerServices ; 
@@ -21,9 +22,12 @@ internal partial struct TypeNameResolver
2122        private  bool  _extensibleParser ; 
2223        private  bool  _requireAssemblyQualifiedName ; 
2324        private  bool  _suppressContextualReflectionContext ; 
25+         private  IntPtr  _unsafeAccessorMethod ; 
2426        private  Assembly ?  _requestingAssembly ; 
2527        private  Assembly ?  _topLevelAssembly ; 
2628
29+         private  bool  SupportsUnboundGenerics  {  get  =>  _unsafeAccessorMethod  !=  IntPtr . Zero ;  } 
30+ 
2731        [ RequiresUnreferencedCode ( "The type might be removed" ) ] 
2832        internal  static   Type ?  GetType ( 
2933            string  typeName , 
@@ -128,14 +132,14 @@ internal static RuntimeType GetTypeReferencedByCustomAttribute(string typeName,
128132
129133        // Used by VM 
130134        internal  static   unsafe  RuntimeType ?  GetTypeHelper ( char *  pTypeName ,  RuntimeAssembly ?  requestingAssembly , 
131-             bool  throwOnError ,  bool  requireAssemblyQualifiedName ) 
135+             bool  throwOnError ,  bool  requireAssemblyQualifiedName ,   IntPtr   unsafeAccessorMethod ) 
132136        { 
133137            ReadOnlySpan < char >  typeName  =  MemoryMarshal . CreateReadOnlySpanFromNullTerminated ( pTypeName ) ; 
134-             return  GetTypeHelper ( typeName ,  requestingAssembly ,  throwOnError ,  requireAssemblyQualifiedName ) ; 
138+             return  GetTypeHelper ( typeName ,  requestingAssembly ,  throwOnError ,  requireAssemblyQualifiedName ,   unsafeAccessorMethod ) ; 
135139        } 
136140
137141        internal  static   unsafe  RuntimeType ?  GetTypeHelper ( ReadOnlySpan < char >  typeName ,  RuntimeAssembly ?  requestingAssembly , 
138-             bool  throwOnError ,  bool  requireAssemblyQualifiedName ) 
142+             bool  throwOnError ,  bool  requireAssemblyQualifiedName ,   IntPtr   unsafeAccessorMethod   =   0 ) 
139143        { 
140144            // Compat: Empty name throws TypeLoadException instead of 
141145            // the natural ArgumentException 
@@ -158,6 +162,7 @@ internal static RuntimeType GetTypeReferencedByCustomAttribute(string typeName,
158162                _throwOnError  =  throwOnError , 
159163                _suppressContextualReflectionContext  =  true , 
160164                _requireAssemblyQualifiedName  =  requireAssemblyQualifiedName , 
165+                 _unsafeAccessorMethod  =  unsafeAccessorMethod , 
161166            } . Resolve ( parsed ) ; 
162167
163168            if  ( type  !=  null ) 
@@ -186,6 +191,9 @@ internal static RuntimeType GetTypeReferencedByCustomAttribute(string typeName,
186191            return  assembly ; 
187192        } 
188193
194+         [ LibraryImport ( RuntimeHelpers . QCall ,  EntryPoint  =  "UnsafeAccessors_ResolveGenericParamToTypeHandle" ) ] 
195+         private  static   partial  IntPtr  ResolveGenericParamToTypeHandle ( IntPtr  unsafeAccessorMethod ,  [ MarshalAs ( UnmanagedType . Bool ) ]  bool  isMethodParam ,  uint  paramIndex ) ; 
196+ 
189197        [ UnconditionalSuppressMessage ( "ReflectionAnalysis" ,  "IL2026:RequiresUnreferencedCode" , 
190198            Justification  =  "TypeNameResolver.GetType is marked as RequiresUnreferencedCode." ) ] 
191199        [ UnconditionalSuppressMessage ( "ReflectionAnalysis" ,  "IL2075:UnrecognizedReflectionPattern" , 
@@ -228,6 +236,42 @@ internal static RuntimeType GetTypeReferencedByCustomAttribute(string typeName,
228236            { 
229237                if  ( assembly  is  null ) 
230238                { 
239+                     if  ( SupportsUnboundGenerics 
240+                         &&  ! string . IsNullOrEmpty ( escapedTypeName ) 
241+                         &&  escapedTypeName [ 0 ]  ==  '!' ) 
242+                     { 
243+                         Debug . Assert ( _throwOnError ) ;  // Unbound generic support currently always throws. 
244+ 
245+                         // Parse the type as an unbound generic parameter. Following the common VAR/MVAR IL syntax: 
246+                         //  !<Number> - Represents a zero-based index into the type's generic parameters. 
247+                         //  !!<Number> - Represents a zero-based index into the method's generic parameters. 
248+ 
249+                         // Confirm we have at least one more character 
250+                         if  ( escapedTypeName . Length  ==  1 ) 
251+                         { 
252+                             throw  new  TypeLoadException ( SR . Format ( SR . TypeLoad_ResolveType ,  escapedTypeName ) ,  typeName :  escapedTypeName ) ; 
253+                         } 
254+ 
255+                         // At this point we expect either another '!' and then a number or a number. 
256+                         bool  isMethodParam  =  escapedTypeName [ 1 ]  ==  '!' ; 
257+                         ReadOnlySpan < char >  toParse  =  isMethodParam 
258+                             ?  escapedTypeName . AsSpan ( 2 )   // Skip over "!!" 
259+                             :  escapedTypeName . AsSpan ( 1 ) ;  // Skip over "!" 
260+                         if  ( ! uint . TryParse ( toParse ,  NumberStyles . None ,  null ,  out  uint  paramIndex ) ) 
261+                         { 
262+                             throw  new  TypeLoadException ( SR . Format ( SR . TypeLoad_ResolveType ,  escapedTypeName ) ,  typeName :  escapedTypeName ) ; 
263+                         } 
264+ 
265+                         Debug . Assert ( _unsafeAccessorMethod  !=  IntPtr . Zero ) ; 
266+                         IntPtr  typeHandle  =  ResolveGenericParamToTypeHandle ( _unsafeAccessorMethod ,  isMethodParam ,  paramIndex ) ; 
267+                         if  ( typeHandle  ==  IntPtr . Zero ) 
268+                         { 
269+                             throw  new  TypeLoadException ( SR . Format ( SR . TypeLoad_ResolveType ,  escapedTypeName ) ,  typeName :  escapedTypeName ) ; 
270+                         } 
271+ 
272+                         return  RuntimeTypeHandle . GetRuntimeTypeFromHandle ( typeHandle ) ; 
273+                     } 
274+ 
231275                    if  ( _requireAssemblyQualifiedName ) 
232276                    { 
233277                        if  ( _throwOnError ) 
0 commit comments