diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
index 1592a5b94bd26..b5cd4894cd5be 100644
--- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -200,6 +200,7 @@
+
@@ -237,7 +238,6 @@
-
Common\System\Collections\Generic\ArrayBuilder.cs
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs
index b05c69089603a..c8266e25e66d8 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeAssembly.cs
@@ -190,37 +190,65 @@ public override MethodInfo? EntryPoint
}
}
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_GetType", StringMarshalling = StringMarshalling.Utf16)]
- private static partial void GetType(QCallAssembly assembly,
- string name,
- [MarshalAs(UnmanagedType.Bool)] bool throwOnError,
- [MarshalAs(UnmanagedType.Bool)] bool ignoreCase,
- ObjectHandleOnStack type,
- ObjectHandleOnStack keepAlive,
- ObjectHandleOnStack assemblyLoadContext);
+ // For case-sensitive lookups, marshal the strings directly to Utf8 to avoid unnecessary string copies.
+ [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_GetTypeCore", StringMarshalling = StringMarshalling.Utf8)]
+ private static partial void GetTypeCore(QCallAssembly assembly,
+ string typeName,
+ ReadOnlySpan nestedTypeNames,
+ int nestedTypeNamesLength,
+ ObjectHandleOnStack retType);
+
+ [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_GetTypeCoreIgnoreCase", StringMarshalling = StringMarshalling.Utf16)]
+ private static partial void GetTypeCoreIgnoreCase(QCallAssembly assembly,
+ string typeName,
+ ReadOnlySpan nestedTypeNames,
+ int nestedTypeNamesLength,
+ ObjectHandleOnStack retType);
+
+ internal Type? GetTypeCore(string typeName, ReadOnlySpan nestedTypeNames, bool throwOnError, bool ignoreCase)
+ {
+ RuntimeAssembly runtimeAssembly = this;
+ Type? type = null;
+
+ try
+ {
+ if (ignoreCase)
+ {
+ GetTypeCoreIgnoreCase(new QCallAssembly(ref runtimeAssembly),
+ typeName,
+ nestedTypeNames,
+ nestedTypeNames.Length,
+ ObjectHandleOnStack.Create(ref type));
+ }
+ else
+ {
+ GetTypeCore(new QCallAssembly(ref runtimeAssembly),
+ typeName,
+ nestedTypeNames,
+ nestedTypeNames.Length,
+ ObjectHandleOnStack.Create(ref type));
+ }
+ }
+ catch (FileNotFoundException) when (!throwOnError)
+ {
+ return null;
+ }
+
+ if (type == null && throwOnError)
+ throw new TypeLoadException(SR.Format(SR.ClassLoad_General /* TypeLoad_TypeNotFoundInAssembly */, typeName, FullName));
+
+ return type;
+ }
[RequiresUnreferencedCode("Types might be removed")]
public override Type? GetType(
string name, // throw on null strings regardless of the value of "throwOnError"
bool throwOnError, bool ignoreCase)
{
- ArgumentNullException.ThrowIfNull(name);
-
- RuntimeType? type = null;
- object? keepAlive = null;
- AssemblyLoadContext? assemblyLoadContextStack = AssemblyLoadContext.CurrentContextualReflectionContext;
+ ArgumentException.ThrowIfNullOrEmpty(name);
- RuntimeAssembly runtimeAssembly = this;
- GetType(new QCallAssembly(ref runtimeAssembly),
- name,
- throwOnError,
- ignoreCase,
- ObjectHandleOnStack.Create(ref type),
- ObjectHandleOnStack.Create(ref keepAlive),
- ObjectHandleOnStack.Create(ref assemblyLoadContextStack));
- GC.KeepAlive(keepAlive);
-
- return type;
+ return TypeNameParser.GetType(name, topLevelAssembly: this,
+ throwOnError: throwOnError, ignoreCase: ignoreCase);
}
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AssemblyNative_GetExportedTypes")]
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs
index 805cc3c46a342..77cf4cdf15440 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.cs
@@ -420,14 +420,10 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont
string className, // throw on null strings regardless of the value of "throwOnError"
bool throwOnError, bool ignoreCase)
{
- ArgumentNullException.ThrowIfNull(className);
+ ArgumentException.ThrowIfNullOrEmpty(className);
- RuntimeType? retType = null;
- object? keepAlive = null;
- RuntimeModule thisAsLocal = this;
- GetType(new QCallModule(ref thisAsLocal), className, throwOnError, ignoreCase, ObjectHandleOnStack.Create(ref retType), ObjectHandleOnStack.Create(ref keepAlive));
- GC.KeepAlive(keepAlive);
- return retType;
+ return TypeNameParser.GetType(className, topLevelAssembly: Assembly,
+ throwOnError: throwOnError, ignoreCase: ignoreCase);
}
[RequiresAssemblyFiles(UnknownStringMessageInRAF)]
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs
new file mode 100644
index 0000000000000..17a9bf1ba37d6
--- /dev/null
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs
@@ -0,0 +1,234 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.Loader;
+using System.Text;
+using System.Threading;
+
+namespace System.Reflection
+{
+ internal unsafe ref partial struct TypeNameParser
+ {
+ private Func? _assemblyResolver;
+ private Func? _typeResolver;
+ private bool _throwOnError;
+ private bool _ignoreCase;
+ private bool _extensibleParser;
+ private Assembly? _requestingAssembly;
+ private Assembly? _topLevelAssembly;
+
+ [RequiresUnreferencedCode("The type might be removed")]
+ internal static Type? GetType(
+ string typeName,
+ Assembly requestingAssembly,
+ bool throwOnError = false,
+ bool ignoreCase = false)
+ {
+ return GetType(typeName, assemblyResolver: null, typeResolver: null, requestingAssembly: requestingAssembly,
+ throwOnError: throwOnError, ignoreCase: ignoreCase, extensibleParser: false);
+ }
+
+ [RequiresUnreferencedCode("The type might be removed")]
+ internal static Type? GetType(
+ string typeName,
+ Func? assemblyResolver,
+ Func? typeResolver,
+ Assembly? requestingAssembly,
+ bool throwOnError = false,
+ bool ignoreCase = false,
+ bool extensibleParser = true)
+ {
+ ArgumentNullException.ThrowIfNull(typeName);
+
+ // Compat: Empty name throws TypeLoadException instead of
+ // the natural ArgumentException
+ if (typeName.Length == 0)
+ {
+ if (throwOnError)
+ throw new TypeLoadException(SR.Arg_TypeLoadNullStr);
+ return null;
+ }
+
+ return new TypeNameParser(typeName)
+ {
+ _assemblyResolver = assemblyResolver,
+ _typeResolver = typeResolver,
+ _throwOnError = throwOnError,
+ _ignoreCase = ignoreCase,
+ _extensibleParser = extensibleParser,
+ _requestingAssembly = requestingAssembly
+ }.Parse();
+ }
+
+ [RequiresUnreferencedCode("The type might be removed")]
+ internal static Type? GetType(
+ string typeName,
+ bool throwOnError,
+ bool ignoreCase,
+ Assembly topLevelAssembly)
+ {
+ return new TypeNameParser(typeName)
+ {
+ _throwOnError = throwOnError,
+ _ignoreCase = ignoreCase,
+ _topLevelAssembly = topLevelAssembly,
+ _requestingAssembly = topLevelAssembly
+ }.Parse();
+ }
+
+ private bool CheckTopLevelAssemblyQualifiedName()
+ {
+ if (_topLevelAssembly is not null)
+ {
+ if (_throwOnError)
+ throw new ArgumentException(SR.Argument_AssemblyGetTypeCannotSpecifyAssembly);
+ return false;
+ }
+ return true;
+ }
+
+ private Assembly? ResolveAssembly(string assemblyName)
+ {
+ Assembly? assembly;
+ if (_assemblyResolver is not null)
+ {
+ assembly = _assemblyResolver(new AssemblyName(assemblyName));
+ if (assembly is null && _throwOnError)
+ {
+ throw new FileNotFoundException(SR.Format(SR.FileNotFound_ResolveAssembly, assemblyName));
+ }
+ }
+ else
+ {
+ assembly = RuntimeAssembly.InternalLoad(new AssemblyName(assemblyName), ref Unsafe.NullRef(), AssemblyLoadContext.CurrentContextualReflectionContext,
+ requestingAssembly: (RuntimeAssembly?)_requestingAssembly, throwOnFileNotFound: _throwOnError);
+ }
+ return assembly;
+ }
+
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
+ Justification = "TypeNameParser.GetType is marked as RequiresUnreferencedCode.")]
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
+ Justification = "TypeNameParser.GetType is marked as RequiresUnreferencedCode.")]
+ private Type? GetType(string typeName, ReadOnlySpan nestedTypeNames, string? assemblyNameIfAny)
+ {
+ Assembly? assembly;
+
+ if (assemblyNameIfAny is not null)
+ {
+ assembly = ResolveAssembly(assemblyNameIfAny);
+ if (assembly is null)
+ return null;
+ }
+ else
+ {
+ assembly = _topLevelAssembly;
+ }
+
+ Type? type;
+
+ // Resolve the top level type.
+ if (_typeResolver is not null)
+ {
+ string escapedTypeName = EscapeTypeName(typeName);
+
+ type = _typeResolver(assembly, escapedTypeName, _ignoreCase);
+
+ if (type is null)
+ {
+ if (_throwOnError)
+ {
+ throw new TypeLoadException(assembly is null ?
+ SR.Format(SR.TypeLoad_ResolveType, escapedTypeName) :
+ SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, escapedTypeName, assembly.FullName));
+ }
+ return null;
+ }
+ }
+ else
+ {
+ if (assembly is null)
+ {
+ return GetTypeFromDefaultAssemblies(typeName, nestedTypeNames);
+ }
+
+ if (assembly is RuntimeAssembly runtimeAssembly)
+ {
+ // Compat: Non-extensible parser allows ambiguous matches with ignore case lookup
+ if (!_extensibleParser || !_ignoreCase)
+ {
+ return runtimeAssembly.GetTypeCore(typeName, nestedTypeNames, throwOnError: _throwOnError, ignoreCase: _ignoreCase);
+ }
+ type = runtimeAssembly.GetTypeCore(typeName, default, throwOnError: _throwOnError, ignoreCase: _ignoreCase);
+ }
+ else
+ {
+ // This is a third-party Assembly object. Emulate GetTypeCore() by calling the public GetType()
+ // method. This is wasteful because it'll probably reparse a type string that we've already parsed
+ // but it can't be helped.
+ type = assembly.GetType(EscapeTypeName(typeName), throwOnError: _throwOnError, ignoreCase: _ignoreCase);
+ }
+
+ if (type is null)
+ return null;
+ }
+
+ for (int i = 0; i < nestedTypeNames.Length; i++)
+ {
+ BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public;
+ if (_ignoreCase)
+ bindingFlags |= BindingFlags.IgnoreCase;
+
+ type = type.GetNestedType(nestedTypeNames[i], bindingFlags);
+
+ if (type is null)
+ {
+ if (_throwOnError)
+ {
+ throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveNestedType,
+ nestedTypeNames[i], (i > 0) ? nestedTypeNames[i - 1] : typeName));
+ }
+ return null;
+ }
+ }
+
+ return type;
+ }
+
+ private Type? GetTypeFromDefaultAssemblies(string typeName, ReadOnlySpan nestedTypeNames)
+ {
+ RuntimeAssembly? requestingAssembly = (RuntimeAssembly?)_requestingAssembly;
+ if (requestingAssembly is not null)
+ {
+ Type? type = ((RuntimeAssembly)requestingAssembly).GetTypeCore(typeName, nestedTypeNames, throwOnError: false, ignoreCase: _ignoreCase);
+ if (type is not null)
+ return type;
+ }
+
+ RuntimeAssembly coreLib = (RuntimeAssembly)typeof(object).Assembly;
+ if (requestingAssembly != coreLib)
+ {
+ Type? type = ((RuntimeAssembly)coreLib).GetTypeCore(typeName, nestedTypeNames, throwOnError: false, ignoreCase: _ignoreCase);
+ if (type is not null)
+ return type;
+ }
+
+ RuntimeAssembly? resolvedAssembly = AssemblyLoadContext.OnTypeResolve(requestingAssembly, EscapeTypeName(typeName, nestedTypeNames));
+ if (resolvedAssembly is not null)
+ {
+ Type? type = resolvedAssembly.GetTypeCore(typeName, nestedTypeNames, throwOnError: false, ignoreCase: _ignoreCase);
+ if (type is not null)
+ return type;
+ }
+
+ if (_throwOnError)
+ throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, EscapeTypeName(typeName), (requestingAssembly ?? coreLib).FullName));
+
+ return null;
+ }
+ }
+}
diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
index 6e83ce5b8a795..3d66c0b717336 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
@@ -544,12 +544,6 @@ private static partial void GetTypeByName(string name, [MarshalAs(UnmanagedType.
ObjectHandleOnStack assemblyLoadContext,
ObjectHandleOnStack type, ObjectHandleOnStack keepalive);
- // Wrapper function to reduce the need for ifdefs.
- internal static RuntimeType? GetTypeByName(string name, bool throwOnError, bool ignoreCase, ref StackCrawlMark stackMark)
- {
- return GetTypeByName(name, throwOnError, ignoreCase, ref stackMark, AssemblyLoadContext.CurrentContextualReflectionContext!);
- }
-
internal static RuntimeType? GetTypeByName(string name, bool throwOnError, bool ignoreCase, ref StackCrawlMark stackMark,
AssemblyLoadContext assemblyLoadContext)
{
diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
index 7a159785405ca..872f9d08b1910 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
@@ -1779,15 +1779,6 @@ internal FieldInfo GetField(RuntimeFieldHandleInternal field)
#region Static Members
#region Internal
- internal static RuntimeType? GetType(string typeName, bool throwOnError, bool ignoreCase,
- ref StackCrawlMark stackMark)
- {
- ArgumentNullException.ThrowIfNull(typeName);
-
- return RuntimeTypeHandle.GetTypeByName(
- typeName, throwOnError, ignoreCase, ref stackMark);
- }
-
[RequiresUnreferencedCode("Trimming changes metadata tokens")]
internal static MethodBase? GetMethodBase(RuntimeModule scope, int typeMetadataToken)
{
@@ -1808,7 +1799,7 @@ internal FieldInfo GetField(RuntimeFieldHandleInternal field)
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The code in this method looks up the method by name, but it always starts with a method handle." +
- "To get here something somwhere had to get the method handle and thus the method must exist.")]
+ "To get here something somewhere had to get the method handle and thus the method must exist.")]
internal static MethodBase? GetMethodBase(RuntimeType? reflectedType, RuntimeMethodHandleInternal methodHandle)
{
Debug.Assert(!methodHandle.IsNullHandle());
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Type.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Type.CoreCLR.cs
index fa00bf705863f..fe4b8622b5633 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Type.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Type.CoreCLR.cs
@@ -26,7 +26,8 @@ public bool IsInterface
public static Type? GetType(string typeName, bool throwOnError, bool ignoreCase)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return RuntimeType.GetType(typeName, throwOnError, ignoreCase, ref stackMark);
+ return TypeNameParser.GetType(typeName, Assembly.GetExecutingAssembly(ref stackMark),
+ throwOnError: throwOnError, ignoreCase: ignoreCase);
}
[RequiresUnreferencedCode("The type might be removed")]
@@ -34,7 +35,8 @@ public bool IsInterface
public static Type? GetType(string typeName, bool throwOnError)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return RuntimeType.GetType(typeName, throwOnError, false, ref stackMark);
+ return TypeNameParser.GetType(typeName, Assembly.GetExecutingAssembly(ref stackMark),
+ throwOnError: throwOnError);
}
[RequiresUnreferencedCode("The type might be removed")]
@@ -42,7 +44,7 @@ public bool IsInterface
public static Type? GetType(string typeName)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return RuntimeType.GetType(typeName, false, false, ref stackMark);
+ return TypeNameParser.GetType(typeName, Assembly.GetExecutingAssembly(ref stackMark));
}
[RequiresUnreferencedCode("The type might be removed")]
@@ -53,7 +55,8 @@ public bool IsInterface
Func? typeResolver)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return TypeNameParser.GetType(typeName, assemblyResolver, typeResolver, false, false, ref stackMark);
+ return TypeNameParser.GetType(typeName, assemblyResolver, typeResolver,
+ ((assemblyResolver != null) && (typeResolver != null)) ? null : Assembly.GetExecutingAssembly(ref stackMark));
}
[RequiresUnreferencedCode("The type might be removed")]
@@ -65,7 +68,9 @@ public bool IsInterface
bool throwOnError)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return TypeNameParser.GetType(typeName, assemblyResolver, typeResolver, throwOnError, false, ref stackMark);
+ return TypeNameParser.GetType(typeName, assemblyResolver, typeResolver,
+ ((assemblyResolver != null) && (typeResolver != null)) ? null : Assembly.GetExecutingAssembly(ref stackMark),
+ throwOnError : throwOnError);
}
[RequiresUnreferencedCode("The type might be removed")]
@@ -78,7 +83,9 @@ public bool IsInterface
bool ignoreCase)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return TypeNameParser.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, ref stackMark);
+ return TypeNameParser.GetType(typeName, assemblyResolver, typeResolver,
+ ((assemblyResolver != null) && (typeResolver != null)) ? null : Assembly.GetExecutingAssembly(ref stackMark),
+ throwOnError: throwOnError, ignoreCase: ignoreCase);
}
// Given a class handle, this will return the class for that handle.
diff --git a/src/coreclr/System.Private.CoreLib/src/System/TypeNameParser.cs b/src/coreclr/System.Private.CoreLib/src/System/TypeNameParser.cs
deleted file mode 100644
index 06f37043a5b24..0000000000000
--- a/src/coreclr/System.Private.CoreLib/src/System/TypeNameParser.cs
+++ /dev/null
@@ -1,341 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Runtime.Loader;
-using System.Text;
-using System.Threading;
-using Microsoft.Win32.SafeHandles;
-
-namespace System
-{
- internal sealed partial class SafeTypeNameParserHandle : SafeHandleZeroOrMinusOneIsInvalid
- {
- #region QCalls
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeName_ReleaseTypeNameParser")]
- private static partial void Release(IntPtr pTypeNameParser);
- #endregion
-
- public SafeTypeNameParserHandle()
- : base(true)
- {
- }
-
- protected override bool ReleaseHandle()
- {
- Release(handle);
- handle = IntPtr.Zero;
- return true;
- }
- }
-
- internal sealed partial class TypeNameParser : IDisposable
- {
- #region QCalls
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeName_CreateTypeNameParser", StringMarshalling = StringMarshalling.Utf16)]
- private static partial void _CreateTypeNameParser(string typeName, ObjectHandleOnStack retHandle, [MarshalAs(UnmanagedType.Bool)] bool throwOnError);
-
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeName_GetNames")]
- private static partial void _GetNames(SafeTypeNameParserHandle pTypeNameParser, ObjectHandleOnStack retArray);
-
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeName_GetTypeArguments")]
- private static partial void _GetTypeArguments(SafeTypeNameParserHandle pTypeNameParser, ObjectHandleOnStack retArray);
-
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeName_GetModifiers")]
- private static partial void _GetModifiers(SafeTypeNameParserHandle pTypeNameParser, ObjectHandleOnStack retArray);
-
- [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "TypeName_GetAssemblyName")]
- private static partial void _GetAssemblyName(SafeTypeNameParserHandle pTypeNameParser, StringHandleOnStack retString);
- #endregion
-
- #region Static Members
- [RequiresUnreferencedCode("The type might be removed")]
- internal static Type? GetType(
- string typeName,
- Func? assemblyResolver,
- Func? typeResolver,
- bool throwOnError,
- bool ignoreCase,
- ref StackCrawlMark stackMark)
- {
- ArgumentNullException.ThrowIfNull(typeName);
-
- if (typeName.Length > 0 && typeName[0] == '\0')
- throw new ArgumentException(SR.Format_StringZeroLength);
-
- Type? ret = null;
-
- SafeTypeNameParserHandle? handle = CreateTypeNameParser(typeName, throwOnError);
-
- if (handle != null)
- {
- // If we get here the typeName must have been successfully parsed.
- // Let's construct the Type object.
- using (TypeNameParser parser = new TypeNameParser(handle))
- {
- ret = parser.ConstructType(assemblyResolver, typeResolver, throwOnError, ignoreCase, ref stackMark);
- }
- }
-
- return ret;
- }
- #endregion
-
- #region Private Data Members
- private readonly SafeTypeNameParserHandle m_NativeParser;
- private static readonly IndexOfAnyValues s_specialChars = IndexOfAnyValues.Create(",[]&*+\\"); // see typeparse.h
- #endregion
-
- #region Constructor and Disposer
- private TypeNameParser(SafeTypeNameParserHandle handle)
- {
- m_NativeParser = handle;
- }
-
- public void Dispose()
- {
- m_NativeParser.Dispose();
- }
- #endregion
-
- #region private Members
- [RequiresUnreferencedCode("The type might be removed")]
- private unsafe Type? ConstructType(
- Func? assemblyResolver,
- Func? typeResolver,
- bool throwOnError,
- bool ignoreCase,
- ref StackCrawlMark stackMark)
- {
- // assembly name
- Assembly? assembly = null;
- string asmName = GetAssemblyName();
-
- // GetAssemblyName never returns null
- Debug.Assert(asmName != null);
-
- if (asmName.Length > 0)
- {
- assembly = ResolveAssembly(asmName, assemblyResolver, throwOnError, ref stackMark);
-
- if (assembly == null)
- {
- // Cannot resolve the assembly. If throwOnError is true we should have already thrown.
- return null;
- }
- }
-
- string[]? names = GetNames();
- if (names == null)
- {
- // This can only happen if the type name is an empty string or if the first char is '\0'
- if (throwOnError)
- throw new TypeLoadException(SR.Arg_TypeLoadNullStr);
-
- return null;
- }
-
- Type? baseType = ResolveType(assembly, names, typeResolver, throwOnError, ignoreCase, ref stackMark);
-
- if (baseType == null)
- {
- // Cannot resolve the type. If throwOnError is true we should have already thrown.
- Debug.Assert(!throwOnError);
- return null;
- }
-
- SafeTypeNameParserHandle[]? typeArguments = GetTypeArguments();
-
- Type?[]? types = null;
- if (typeArguments != null)
- {
- types = new Type[typeArguments.Length];
- for (int i = 0; i < typeArguments.Length; i++)
- {
- Debug.Assert(typeArguments[i] != null);
-
- using (TypeNameParser argParser = new TypeNameParser(typeArguments[i]))
- {
- types[i] = argParser.ConstructType(assemblyResolver, typeResolver, throwOnError, ignoreCase, ref stackMark);
- }
-
- if (types[i] == null)
- {
- // If throwOnError is true argParser.ConstructType should have already thrown.
- Debug.Assert(!throwOnError);
- return null;
- }
- }
- }
-
- int[]? modifiers = GetModifiers();
- return RuntimeTypeHandle.GetTypeHelper(baseType, types!, modifiers);
- }
-
- private static Assembly? ResolveAssembly(string asmName, Func? assemblyResolver, bool throwOnError, ref StackCrawlMark stackMark)
- {
- Debug.Assert(!string.IsNullOrEmpty(asmName));
-
- Assembly? assembly;
- if (assemblyResolver == null)
- {
- if (throwOnError)
- {
- assembly = RuntimeAssembly.InternalLoad(asmName, ref stackMark, AssemblyLoadContext.CurrentContextualReflectionContext);
- }
- else
- {
- // When throwOnError is false we should only catch FileNotFoundException.
- // Other exceptions like BadImangeFormatException should still fly.
- try
- {
- assembly = RuntimeAssembly.InternalLoad(asmName, ref stackMark, AssemblyLoadContext.CurrentContextualReflectionContext);
- }
- catch (FileNotFoundException)
- {
- return null;
- }
- }
- }
- else
- {
- assembly = assemblyResolver(new AssemblyName(asmName));
- if (assembly == null && throwOnError)
- {
- throw new FileNotFoundException(SR.Format(SR.FileNotFound_ResolveAssembly, asmName));
- }
- }
-
- return assembly;
- }
-
- [RequiresUnreferencedCode("The type might be removed")]
- private static Type? ResolveType(Assembly? assembly, string[] names, Func? typeResolver, bool throwOnError, bool ignoreCase, ref StackCrawlMark stackMark)
- {
- Debug.Assert(names != null && names.Length > 0);
-
- Type? type;
-
- // both the customer provided and the default type resolvers accept escaped type names
- string OuterMostTypeName = EscapeTypeName(names[0]);
-
- // Resolve the top level type.
- if (typeResolver != null)
- {
- type = typeResolver(assembly, OuterMostTypeName, ignoreCase);
-
- if (type == null && throwOnError)
- {
- string errorString = assembly == null ?
- SR.Format(SR.TypeLoad_ResolveType, OuterMostTypeName) :
- SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, OuterMostTypeName, assembly.FullName);
-
- throw new TypeLoadException(errorString);
- }
- }
- else
- {
- if (assembly == null)
- {
- type = RuntimeType.GetType(OuterMostTypeName, throwOnError, ignoreCase, ref stackMark);
- }
- else
- {
- type = assembly.GetType(OuterMostTypeName, throwOnError, ignoreCase);
- }
- }
-
- // Resolve nested types.
- if (type != null)
- {
- BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public;
- if (ignoreCase)
- bindingFlags |= BindingFlags.IgnoreCase;
-
- for (int i = 1; i < names.Length; i++)
- {
- type = type.GetNestedType(names[i], bindingFlags);
-
- if (type == null)
- {
- if (throwOnError)
- throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveNestedType, names[i], names[i - 1]));
- else
- break;
- }
- }
- }
-
- return type;
- }
-
- private static string EscapeTypeName(string name)
- {
- int specialCharIndex = name.AsSpan().IndexOfAny(s_specialChars);
- if (specialCharIndex < 0)
- {
- return name;
- }
-
- var sb = new ValueStringBuilder(stackalloc char[64]);
- sb.Append(name.AsSpan(0, specialCharIndex));
-
- foreach (char c in name.AsSpan(specialCharIndex))
- {
- if (s_specialChars.Contains(c))
- sb.Append('\\');
-
- sb.Append(c);
- }
-
- return sb.ToString();
- }
-
- private static SafeTypeNameParserHandle? CreateTypeNameParser(string typeName, bool throwOnError)
- {
- SafeTypeNameParserHandle? retHandle = null;
- _CreateTypeNameParser(typeName, ObjectHandleOnStack.Create(ref retHandle), throwOnError);
-
- return retHandle;
- }
-
- private string[]? GetNames()
- {
- string[]? names = null;
- _GetNames(m_NativeParser, ObjectHandleOnStack.Create(ref names));
-
- return names;
- }
-
- private SafeTypeNameParserHandle[]? GetTypeArguments()
- {
- SafeTypeNameParserHandle[]? arguments = null;
- _GetTypeArguments(m_NativeParser, ObjectHandleOnStack.Create(ref arguments));
-
- return arguments;
- }
-
- private int[]? GetModifiers()
- {
- int[]? modifiers = null;
- _GetModifiers(m_NativeParser, ObjectHandleOnStack.Create(ref modifiers));
-
- return modifiers;
- }
-
- private string GetAssemblyName()
- {
- string? assemblyName = null;
- _GetAssemblyName(m_NativeParser, new StringHandleOnStack(ref assemblyName));
-
- return assemblyName!;
- }
- #endregion
- }
-}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/AssemblyBinder.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/AssemblyBinder.cs
index 690fcdafa6722..30383e213c941 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/AssemblyBinder.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/AssemblyBinder.cs
@@ -32,8 +32,6 @@ public partial struct AssemblyBindResult
[CLSCompliant(false)]
public abstract class AssemblyBinder
{
- public const string DefaultAssemblyNameForGetType = "System.Private.CoreLib";
-
public abstract bool Bind(RuntimeAssemblyName refName, bool cacheMissedLookups, out AssemblyBindResult result, out Exception exception);
public abstract bool Bind(ReadOnlySpan rawAssembly, ReadOnlySpan rawSymbolStore, out AssemblyBindResult result, out Exception exception);
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs
index 42db1ce893a91..87e5823a9ee8d 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs
@@ -15,9 +15,8 @@
using System.Reflection.Runtime.TypeInfos.EcmaFormat;
using System.Reflection.Runtime.MethodInfos.EcmaFormat;
#endif
-using System.Reflection.Runtime.TypeParsing;
-using System.Reflection.Runtime.CustomAttributes;
using System.Runtime.CompilerServices;
+
using Internal.Metadata.NativeFormat;
using Internal.Runtime.Augments;
@@ -36,91 +35,6 @@ internal ExecutionDomain(ReflectionDomainSetup executionDomainSetup, ExecutionEn
ReflectionDomainSetup = executionDomainSetup;
}
- //
- // Retrieves a type by name. Helper to implement Type.GetType();
- //
- public Type GetType(string typeName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase, IList defaultAssemblyNames)
- {
- ArgumentNullException.ThrowIfNull(typeName);
-
- if (typeName.Length == 0)
- {
- if (throwOnError)
- throw new TypeLoadException(SR.Arg_TypeLoadNullStr);
- else
- return null;
- }
-
- TypeName parsedName = TypeParser.ParseAssemblyQualifiedTypeName(typeName, throwOnError: throwOnError);
- if (parsedName == null)
- return null;
- CoreAssemblyResolver coreAssemblyResolver = CreateCoreAssemblyResolver(assemblyResolver);
- CoreTypeResolver coreTypeResolver = CreateCoreTypeResolver(typeResolver, defaultAssemblyNames, throwOnError: throwOnError, ignoreCase: ignoreCase);
- GetTypeOptions getTypeOptions = new GetTypeOptions(coreAssemblyResolver, coreTypeResolver, throwOnError: throwOnError, ignoreCase: ignoreCase);
-
- return parsedName.ResolveType(null, getTypeOptions);
- }
-
- private static CoreAssemblyResolver CreateCoreAssemblyResolver(Func assemblyResolver)
- {
- if (assemblyResolver == null)
- {
- return RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists;
- }
- else
- {
- return delegate (RuntimeAssemblyName runtimeAssemblyName)
- {
- AssemblyName assemblyName = runtimeAssemblyName.ToAssemblyName();
- Assembly assembly = assemblyResolver(assemblyName);
- return assembly;
- };
- }
- }
-
- private static CoreTypeResolver CreateCoreTypeResolver(Func typeResolver, IList defaultAssemblyNames, bool throwOnError, bool ignoreCase)
- {
- if (typeResolver == null)
- {
- return delegate (Assembly containingAssemblyIfAny, string coreTypeName)
- {
- if (containingAssemblyIfAny != null)
- {
- return containingAssemblyIfAny.GetTypeCore(coreTypeName, ignoreCase: ignoreCase);
- }
- else
- {
- foreach (string defaultAssemblyName in defaultAssemblyNames)
- {
- RuntimeAssemblyName runtimeAssemblyName = RuntimeAssemblyName.Parse(defaultAssemblyName);
- RuntimeAssemblyInfo defaultAssembly = RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(runtimeAssemblyName);
- if (defaultAssembly == null)
- continue;
- Type resolvedType = defaultAssembly.GetTypeCore(coreTypeName, ignoreCase: ignoreCase);
- if (resolvedType != null)
- return resolvedType;
- }
-
- if (throwOnError && defaultAssemblyNames.Count > 0)
- {
- // Though we don't have to throw a TypeLoadException exception (that's our caller's job), we can throw a more specific exception than he would so just do it.
- throw Helpers.CreateTypeLoadException(coreTypeName, defaultAssemblyNames[0]);
- }
- return null;
- }
- };
- }
- else
- {
- return delegate (Assembly containingAssemblyIfAny, string coreTypeName)
- {
- string escapedName = coreTypeName.EscapeTypeNameIdentifier();
- Type type = typeResolver(containingAssemblyIfAny, escapedName, ignoreCase);
- return type;
- };
- }
- }
-
//
// Retrieves the MethodBase for a given method handle. Helper to implement Delegate.GetMethodInfo()
//
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs
index fdd5ba2dc4eaf..8a7e5316fe107 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/ReflectionExecutionDomainCallbacks.cs
@@ -25,9 +25,6 @@ namespace Internal.Runtime.Augments
[System.Runtime.CompilerServices.ReflectionBlocked]
public abstract class ReflectionExecutionDomainCallbacks
{
- // Api's that are exposed in System.Runtime but are really reflection apis.
- public abstract Type GetType(string typeName, Func? assemblyResolver, Func? typeResolver, bool throwOnError, bool ignoreCase, string defaultAssembly);
-
public abstract IntPtr TryGetStaticClassConstructionContext(RuntimeTypeHandle runtimeTypeHandle);
public abstract bool IsReflectionBlocked(RuntimeTypeHandle typeHandle);
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ReflectionHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ReflectionHelpers.cs
index 16b96d5116abe..6d6c9531e7060 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ReflectionHelpers.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ReflectionHelpers.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Internal.Runtime.Augments;
@@ -17,14 +18,14 @@ internal static class ReflectionHelpers
// a default assembly name.
public static Type GetType(string typeName, string callingAssemblyName, bool throwOnError, bool ignoreCase)
{
- return ExtensibleGetType(typeName, callingAssemblyName, null, null, throwOnError: throwOnError, ignoreCase: ignoreCase);
+ return TypeNameParser.GetType(typeName, throwOnError: throwOnError, ignoreCase: ignoreCase, defaultAssemblyName: callingAssemblyName);
}
// This entry is used to implement Type.GetType()'s ability to detect the calling assembly and use it as
// a default assembly name.
public static Type ExtensibleGetType(string typeName, string callingAssemblyName, Func assemblyResolver, Func? typeResolver, bool throwOnError, bool ignoreCase)
{
- return RuntimeAugments.Callbacks.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, callingAssemblyName);
+ return TypeNameParser.GetType(typeName, assemblyResolver, typeResolver, throwOnError: throwOnError, ignoreCase: ignoreCase, defaultAssemblyName: callingAssemblyName);
}
// This supports Assembly.GetExecutingAssembly() intrinsic expansion in the compiler
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
index 31e77129e95d8..9625b051d1fd0 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
@@ -509,10 +509,7 @@
-
-
-
-
+
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseInsensitive.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseInsensitive.cs
index 48a2198d49bfe..a6a183fa73cd8 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseInsensitive.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseInsensitive.cs
@@ -1,21 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.IO;
-using System.Text;
-using System.Diagnostics;
+using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Runtime.General;
-using System.Reflection.Runtime.Modules;
using System.Reflection.Runtime.TypeInfos;
-using System.Reflection.Runtime.TypeParsing;
-using System.Reflection.Runtime.CustomAttributes;
-using System.Collections.Generic;
-using Internal.Reflection.Core;
-using Internal.Reflection.Core.Execution;
using Internal.Metadata.NativeFormat;
+using Internal.Reflection.Core;
namespace System.Reflection.Runtime.Assemblies.NativeFormat
{
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseSensitive.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseSensitive.cs
index 018358a63180a..859f18cc7445a 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseSensitive.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.GetTypeCore.CaseSensitive.cs
@@ -1,19 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Concurrent;
-using System.Diagnostics;
using System.Reflection;
using System.Reflection.Runtime.General;
-using System.Reflection.Runtime.Modules;
using System.Reflection.Runtime.TypeInfos;
-using System.Reflection.Runtime.TypeParsing;
-using System.Reflection.Runtime.CustomAttributes;
-using System.Collections.Generic;
using Internal.Reflection.Core;
-using Internal.Reflection.Core.Execution;
using Internal.Metadata.NativeFormat;
namespace System.Reflection.Runtime.Assemblies.NativeFormat
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.cs
index 7de909b4f29ba..a0733ee8cbabc 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/NativeFormat/NativeFormatRuntimeAssembly.cs
@@ -1,26 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.IO;
-using System.Text;
-using System.Diagnostics;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.IO;
using System.Reflection;
+using System.Reflection.Runtime.CustomAttributes;
using System.Reflection.Runtime.General;
using System.Reflection.Runtime.MethodInfos;
using System.Reflection.Runtime.MethodInfos.NativeFormat;
-using System.Reflection.Runtime.Modules;
using System.Reflection.Runtime.Modules.NativeFormat;
-using System.Reflection.Runtime.TypeInfos;
using System.Reflection.Runtime.TypeInfos.NativeFormat;
-using System.Reflection.Runtime.TypeParsing;
-using System.Reflection.Runtime.CustomAttributes;
-using System.Collections.Generic;
+using Internal.Metadata.NativeFormat;
using Internal.Reflection.Core;
using Internal.Reflection.Core.Execution;
-using Internal.Metadata.NativeFormat;
namespace System.Reflection.Runtime.Assemblies.NativeFormat
{
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/RuntimeAssemblyInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/RuntimeAssemblyInfo.cs
index fe706ffc09a0b..93c1252f62222 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/RuntimeAssemblyInfo.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/Assemblies/RuntimeAssemblyInfo.cs
@@ -1,26 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
using System.Collections.Concurrent;
-using System.IO;
-using System.Text;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
-using System.Reflection;
-using System.Runtime.Serialization;
+using System.IO;
using System.Reflection.Runtime.General;
-using System.Reflection.Runtime.Modules;
using System.Reflection.Runtime.TypeInfos;
-using System.Reflection.Runtime.TypeParsing;
-using System.Reflection.Runtime.CustomAttributes;
-using System.Collections.Generic;
+using System.Runtime.Serialization;
+using System.Security;
using Internal.Reflection.Core;
using Internal.Reflection.Core.Execution;
-using Internal.Reflection.Core.NonPortable;
-
-using System.Security;
namespace System.Reflection.Runtime.Assemblies
{
@@ -73,29 +65,10 @@ public sealed override Type GetType(string name, bool throwOnError, bool ignoreC
{
ArgumentException.ThrowIfNullOrEmpty(name);
- TypeName typeName = TypeParser.ParseAssemblyQualifiedTypeName(name, throwOnError: throwOnError);
- if (typeName == null)
- return null;
- if (typeName is AssemblyQualifiedTypeName)
- {
- if (throwOnError)
- throw new ArgumentException(SR.Argument_AssemblyGetTypeCannotSpecifyAssembly); // Cannot specify an assembly qualifier in a typename passed to Assembly.GetType()
- else
- return null;
- }
-
- CoreAssemblyResolver coreAssemblyResolver = GetRuntimeAssemblyIfExists;
- CoreTypeResolver coreTypeResolver =
- delegate (Assembly containingAssemblyIfAny, string coreTypeName)
- {
- if (containingAssemblyIfAny == null)
- return GetTypeCore(coreTypeName, ignoreCase: ignoreCase);
- else
- return containingAssemblyIfAny.GetTypeCore(coreTypeName, ignoreCase: ignoreCase);
- };
- GetTypeOptions getTypeOptions = new GetTypeOptions(coreAssemblyResolver, coreTypeResolver, throwOnError: throwOnError, ignoreCase: ignoreCase);
-
- return typeName.ResolveType(this, getTypeOptions);
+ return TypeNameParser.GetType(name,
+ throwOnError: throwOnError,
+ ignoreCase: ignoreCase,
+ topLevelAssembly: this);
}
#pragma warning disable 0067 // Silence warning about ModuleResolve not being used.
@@ -129,9 +102,7 @@ public sealed override Type[] GetForwardedTypes()
Exception exception = TryGetRuntimeAssembly(redirectedAssemblyName, out redirectedAssembly);
if (exception == null)
{
- type = redirectedAssembly.GetTypeCore(fullTypeName, ignoreCase: false); // GetTypeCore() will follow any further type-forwards if needed.
- if (type == null)
- exception = Helpers.CreateTypeLoadException(fullTypeName.EscapeTypeNameIdentifier(), redirectedAssembly);
+ type = redirectedAssembly.GetTypeCore(fullTypeName, throwOnError: true, ignoreCase: false); // GetTypeCore() will follow any further type-forwards if needed.
}
Debug.Assert((type != null) != (exception != null)); // Exactly one of these must be non-null.
@@ -182,12 +153,12 @@ private static void AddPublicNestedTypes(Type type, List types)
///
/// Returns null if the type does not exist. Throws for all other error cases.
///
- internal RuntimeTypeInfo GetTypeCore(string fullName, bool ignoreCase)
+ internal RuntimeTypeInfo GetTypeCore(string fullName, bool throwOnError, bool ignoreCase)
{
- if (ignoreCase)
- return GetTypeCoreCaseInsensitive(fullName);
- else
- return GetTypeCoreCaseSensitive(fullName);
+ RuntimeTypeInfo type = ignoreCase ? GetTypeCoreCaseInsensitive(fullName) : GetTypeCoreCaseSensitive(fullName);
+ if (type == null && throwOnError)
+ throw Helpers.CreateTypeLoadException(fullName, this.FullName);
+ return type;
}
// Types that derive from RuntimeAssembly must implement the following public surface area members
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/Helpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/Helpers.cs
index 62e674979256a..f71dfb4e8cbe7 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/Helpers.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/Helpers.cs
@@ -95,25 +95,6 @@ public static MethodInfo FilterAccessor(this MethodInfo accessor, bool nonPublic
return null;
}
- [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
- Justification = "Calling Assembly.GetType on a third-party Assembly class.")]
- public static Type GetTypeCore(this Assembly assembly, string name, bool ignoreCase)
- {
- if (assembly is RuntimeAssemblyInfo runtimeAssembly)
- {
- // Not a recursion - this one goes to the actual instance method on RuntimeAssembly.
- return runtimeAssembly.GetTypeCore(name, ignoreCase: ignoreCase);
- }
- else
- {
- // This is a third-party Assembly object. We can emulate GetTypeCore() by calling the public GetType()
- // method. This is wasteful because it'll probably reparse a type string that we've already parsed
- // but it can't be helped.
- string escapedName = name.EscapeTypeNameIdentifier();
- return assembly.GetType(escapedName, throwOnError: false, ignoreCase: ignoreCase);
- }
- }
-
public static TypeLoadException CreateTypeLoadException(string typeName, Assembly assemblyIfAny)
{
if (assemblyIfAny == null)
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeResolver.NativeFormat.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeResolver.NativeFormat.cs
index 659185ccd744b..4a580b7646922 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeResolver.NativeFormat.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeResolver.NativeFormat.cs
@@ -1,19 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Text;
-using System.Reflection;
-using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Collections.Generic;
-using System.Reflection.Runtime.General;
using System.Reflection.Runtime.TypeInfos;
using System.Reflection.Runtime.Assemblies;
-using System.Reflection.Runtime.TypeParsing;
-using Internal.Reflection.Core;
using Internal.Reflection.Core.Execution;
using Internal.Metadata.NativeFormat;
@@ -214,7 +207,7 @@ internal static RuntimeTypeInfo ResolveTypeDefinition(this TypeDefinitionHandle
exception = RuntimeAssemblyInfo.TryGetRuntimeAssembly(assemblyName, out runtimeAssembly);
if (exception != null)
return null;
- RuntimeTypeInfo runtimeType = runtimeAssembly.GetTypeCore(fullName, ignoreCase: false);
+ RuntimeTypeInfo runtimeType = runtimeAssembly.GetTypeCore(fullName, throwOnError: false, ignoreCase: false);
if (runtimeType == null)
{
exception = Helpers.CreateTypeLoadException(fullName, assemblyName.FullName);
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeResolver.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeResolver.cs
index 3b29149f85364..0e1b9f01d3465 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeResolver.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/TypeResolver.cs
@@ -1,19 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Text;
-using System.Reflection;
-using System.Diagnostics;
-using System.Collections.Generic;
-
-using System.Reflection.Runtime.General;
using System.Reflection.Runtime.TypeInfos;
-using System.Reflection.Runtime.Assemblies;
-using System.Reflection.Runtime.TypeParsing;
-
-using Internal.Reflection.Core;
-using Internal.Reflection.Core.Execution;
namespace System.Reflection.Runtime.General
{
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/GetTypeOptions.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/GetTypeOptions.cs
deleted file mode 100644
index e0683426ecdb7..0000000000000
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/GetTypeOptions.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.IO;
-using System.Diagnostics;
-using System.Reflection.Runtime.General;
-using System.Reflection.Runtime.Assemblies;
-
-namespace System.Reflection.Runtime.TypeParsing
-{
- ///
- /// Return the assembly matching the refName if one exists. If a matching assembly doesn't exist, return null. Throw for all other errors.
- ///
- internal delegate Assembly CoreAssemblyResolver(RuntimeAssemblyName refName);
-
- ///
- /// Look for a type matching the name inside the provided assembly. If "containingAssemblyIfAny" is null, look in a set of default assemblies. For example, if
- /// this resolver is for the Type.GetType() api, the default assemblies are the assembly that invoked Type.GetType() and SPCL in that order.
- /// If this resolver is for Assembly.GetType(), the default is that assembly. Third-party resolvers can do whatever they want. If no type exists for that name,
- /// return null. Throw for all other errors. The name will be for a top-level named type only. No nested types. No constructed types.
- ///
- ///
- ///
- /// This delegate "should" take an "ignoreCase" parameter too, but pragmatically, every resolver we create is a closure for other reasons so
- /// it's more convenient to let "ignoreCase" be just another variable that's captured in that closure.
- ///
- internal delegate Type CoreTypeResolver(Assembly containingAssemblyIfAny, string name);
-
- //
- // Captures the various options passed to the Type.GetType() family of apis.
- //
- internal sealed class GetTypeOptions
- {
- public GetTypeOptions(CoreAssemblyResolver coreAssemblyResolver, CoreTypeResolver coreTypeResolver, bool throwOnError, bool ignoreCase)
- {
- Debug.Assert(coreAssemblyResolver != null);
- Debug.Assert(coreTypeResolver != null);
-
- _coreAssemblyResolver = coreAssemblyResolver;
- _coreTypeResolver = coreTypeResolver;
- ThrowOnError = throwOnError;
- IgnoreCase = ignoreCase;
- }
-
- public Assembly CoreResolveAssembly(RuntimeAssemblyName name)
- {
- Assembly assembly = _coreAssemblyResolver(name);
- if (assembly == null && ThrowOnError)
- throw new FileNotFoundException(SR.Format(SR.FileNotFound_AssemblyNotFound, name.FullName));
- return assembly;
- }
-
- public Type CoreResolveType(Assembly containingAssemblyIfAny, string name)
- {
- Type type = _coreTypeResolver(containingAssemblyIfAny, name);
- if (type == null && ThrowOnError)
- throw Helpers.CreateTypeLoadException(name.EscapeTypeNameIdentifier(), containingAssemblyIfAny);
- return type;
- }
-
- public bool ThrowOnError { get; }
- public bool IgnoreCase { get; }
-
- private readonly CoreAssemblyResolver _coreAssemblyResolver;
- private readonly CoreTypeResolver _coreTypeResolver;
- }
-}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs
deleted file mode 100644
index 92a3b8a4c44af..0000000000000
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs
+++ /dev/null
@@ -1,243 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Diagnostics;
-using System.Collections;
-using System.Collections.Generic;
-using System.Reflection.Runtime.General;
-using System.Reflection.Runtime.Assemblies;
-
-namespace System.Reflection.Runtime.TypeParsing
-{
- //
- // String tokenizer for typenames passed to the GetType() api's.
- //
- internal sealed class TypeLexer
- {
- public TypeLexer(string s)
- {
- // Turn the string into a char array with a NUL terminator.
- char[] chars = new char[s.Length + 1];
- s.CopyTo(0, chars, 0, s.Length);
- _chars = chars;
- _index = 0;
- }
-
- public TokenType Peek
- {
- get
- {
- SkipWhiteSpace();
- char c = _chars[_index];
- return CharToToken(c);
- }
- }
-
- public TokenType PeekSecond
- {
- get
- {
- SkipWhiteSpace();
- int index = _index + 1;
- while (char.IsWhiteSpace(_chars[index]))
- index++;
- char c = _chars[index];
- return CharToToken(c);
- }
- }
-
-
- public void Skip()
- {
- Debug.Assert(_index != _chars.Length);
- SkipWhiteSpace();
- _index++;
- }
-
- // Return the next token and skip index past it unless already at end of string
- // or the token is not a reserved token.
- public TokenType GetNextToken()
- {
- TokenType tokenType = Peek;
- if (tokenType == TokenType.End || tokenType == TokenType.Other)
- return tokenType;
- Skip();
- return tokenType;
- }
-
- //
- // Lex the next segment as part of a type name. (Do not use for assembly names.)
- //
- // Note that unescaped "."'s do NOT terminate the identifier, but unescaped "+"'s do.
- //
- // Terminated by the first non-escaped reserved character ('[', ']', '+', '&', '*' or ',')
- //
- public string GetNextIdentifier()
- {
- SkipWhiteSpace();
-
- int src = _index;
- char[] buffer = new char[_chars.Length];
- int dst = 0;
- for (;;)
- {
- char c = _chars[src];
- TokenType token = CharToToken(c);
- if (token != TokenType.Other)
- break;
- src++;
- if (c == '\\')
- {
- c = _chars[src];
- if (c != NUL)
- src++;
- if (!c.NeedsEscapingInTypeName())
- {
- // If we got here, a backslash was used to escape a character that is not legal to escape inside a type name.
- //
- // Common sense would dictate throwing an ArgumentException but that's not what the desktop CLR does.
- // The desktop CLR treats this case by returning FALSE from TypeName::TypeNameParser::GetIdentifier().
- // Unfortunately, no one checks this return result. Instead, the CLR keeps parsing (unfortunately, the lexer
- // was left in some strange state by the previous failure but typically, this goes unnoticed) and eventually, tries to resolve
- // a Type whose name is the empty string. When it can't resolve that type, the CLR throws a TypeLoadException()
- // complaining about be unable to find a type with the empty name.
- //
- // To emulate this accidental behavior, we'll throw a special exception that's caught by the TypeParser.
- //
- throw new IllegalEscapeSequenceException();
- }
- }
- buffer[dst++] = c;
- }
-
- _index = src;
- return new string(buffer, 0, dst);
- }
-
- //
- // Lex the next segment as the assembly name at the end of an assembly-qualified type name. (Do not use for
- // assembly names embedded inside generic type arguments.)
- //
- // Terminated by NUL. There are no escape characters defined by the typename lexer (however, AssemblyName
- // does have its own escape rules.)
- //
- public RuntimeAssemblyName GetNextAssemblyName()
- {
- SkipWhiteSpace();
-
- int src = _index;
- char[] buffer = new char[_chars.Length];
- int dst = 0;
- for (;;)
- {
- char c = _chars[src];
- if (c == NUL)
- break;
- src++;
- buffer[dst++] = c;
- }
- _index = src;
- string fullName = new string(buffer, 0, dst);
- return RuntimeAssemblyName.Parse(fullName);
- }
-
- //
- // Lex the next segment as an assembly name embedded inside a generic argument type.
- //
- // Terminated by an unescaped ']'.
- //
- public RuntimeAssemblyName GetNextEmbeddedAssemblyName()
- {
- SkipWhiteSpace();
-
- int src = _index;
- char[] buffer = new char[_chars.Length];
- int dst = 0;
- for (;;)
- {
- char c = _chars[src];
- if (c == NUL)
- throw new ArgumentException();
- if (c == ']')
- break;
- src++;
-
- // Backslash can be used to escape a ']' - any other backslash character is left alone (along with the backslash)
- // for the AssemblyName parser to handle.
- if (c == '\\' && _chars[src] == ']')
- {
- c = _chars[src++];
- }
- buffer[dst++] = c;
- }
- _index = src;
- string fullName = new string(buffer, 0, dst);
- return RuntimeAssemblyName.Parse(fullName);
- }
-
- //
- // Classify a character as a TokenType. (Fortunately, all tokens in typename strings other than identifiers are single-character tokens.)
- //
- private static TokenType CharToToken(char c)
- {
- switch (c)
- {
- case NUL:
- return TokenType.End;
- case '[':
- return TokenType.OpenSqBracket;
- case ']':
- return TokenType.CloseSqBracket;
- case ',':
- return TokenType.Comma;
- case '+':
- return TokenType.Plus;
- case '*':
- return TokenType.Asterisk;
- case '&':
- return TokenType.Ampersand;
- default:
- return TokenType.Other;
- }
- }
-
- //
- // The desktop typename parser has a strange attitude towards whitespace. It throws away whitespace between punctuation tokens and whitespace
- // preceding identifiers or assembly names (and this cannot be escaped away). But whitespace between the end of an identifier
- // and the punctuation that ends it is *not* ignored.
- //
- // In other words, GetType(" Foo") searches for "Foo" but GetType("Foo ") searches for "Foo ".
- //
- // Whitespace between the end of an assembly name and the punction mark that ends it is also not ignored by this parser,
- // but this is irrelevant since the assembly name is then turned over to AssemblyName for parsing, which *does* ignore trailing whitespace.
- //
- private void SkipWhiteSpace()
- {
- while (char.IsWhiteSpace(_chars[_index]))
- _index++;
- }
-
-
- private int _index;
- private readonly char[] _chars;
- private const char NUL = (char)0;
-
-
- public sealed class IllegalEscapeSequenceException : Exception
- {
- }
- }
-
- internal enum TokenType
- {
- End = 0, //At end of string
- OpenSqBracket = 1, //'['
- CloseSqBracket = 2, //']'
- Comma = 3, //','
- Plus = 4, //'+'
- Asterisk = 5, //'*'
- Ampersand = 6, //'&'
- Other = 7, //Type identifier, AssemblyName or embedded AssemblyName.
- }
-}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeName.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeName.cs
deleted file mode 100644
index f4628ac0998c2..0000000000000
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeName.cs
+++ /dev/null
@@ -1,322 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Collections;
-using System.Reflection;
-using System.Collections.Generic;
-
-using System.Reflection.Runtime.General;
-using System.Reflection.Runtime.TypeInfos;
-using System.Reflection.Runtime.Assemblies;
-
-namespace System.Reflection.Runtime.TypeParsing
-{
- //
- // The TypeName class is the base class for a family of types that represent the nodes in a parse tree for
- // assembly-qualified type names.
- //
- internal abstract class TypeName
- {
- ///
- /// Helper for the Type.GetType() family of apis. "containingAssemblyIsAny" is the assembly to search for (as determined
- /// by a qualifying assembly string in the original type string passed to Type.GetType(). If null, it means the type stream
- /// didn't specify an assembly name. How to respond to that is up to the type resolver delegate in getTypeOptions - this class
- /// is just a middleman.
- ///
- public abstract Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions);
- public abstract override string ToString();
- }
-
- //
- // Represents a parse of a type name qualified by an assembly name.
- //
- internal sealed class AssemblyQualifiedTypeName : TypeName
- {
- public AssemblyQualifiedTypeName(NonQualifiedTypeName nonQualifiedTypeName, RuntimeAssemblyName assemblyName)
- {
- Debug.Assert(nonQualifiedTypeName != null);
- Debug.Assert(assemblyName != null);
- _nonQualifiedTypeName = nonQualifiedTypeName;
- _assemblyName = assemblyName;
- }
-
- public sealed override string ToString()
- {
- return _nonQualifiedTypeName.ToString() + ", " + _assemblyName.FullName;
- }
-
- public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions)
- {
- containingAssemblyIfAny = getTypeOptions.CoreResolveAssembly(_assemblyName);
- if (containingAssemblyIfAny == null)
- return null;
- return _nonQualifiedTypeName.ResolveType(containingAssemblyIfAny, getTypeOptions);
- }
-
- private readonly RuntimeAssemblyName _assemblyName;
- private readonly NonQualifiedTypeName _nonQualifiedTypeName;
- }
-
- //
- // Base class for all non-assembly-qualified type names.
- //
- internal abstract class NonQualifiedTypeName : TypeName
- {
- }
-
- //
- // Base class for namespace or nested type.
- //
- internal abstract class NamedTypeName : NonQualifiedTypeName
- {
- }
-
- //
- // Non-nested named type. The full name is the namespace-qualified name. For example, the FullName for
- // System.Collections.Generic.IList<> is "System.Collections.Generic.IList`1".
- //
- internal sealed partial class NamespaceTypeName : NamedTypeName
- {
- public NamespaceTypeName(string fullName)
- {
- _fullName = fullName;
- }
-
- public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions)
- {
- return getTypeOptions.CoreResolveType(containingAssemblyIfAny, _fullName);
- }
-
- public sealed override string ToString()
- {
- return _fullName.EscapeTypeNameIdentifier();
- }
-
- private readonly string _fullName;
- }
-
- //
- // A nested type. The Name is the simple name of the type (not including any portion of its declaring type name.)
- //
- internal sealed class NestedTypeName : NamedTypeName
- {
- public NestedTypeName(string nestedTypeName, NamedTypeName declaringType)
- {
- _nestedTypeName = nestedTypeName;
- _declaringType = declaringType;
- }
-
- public sealed override string ToString()
- {
- return _declaringType + "+" + _nestedTypeName.EscapeTypeNameIdentifier();
- }
-
- [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
- Justification = "Reflection implementation")]
- public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions)
- {
- Type declaringType = _declaringType.ResolveType(containingAssemblyIfAny, getTypeOptions);
- if (declaringType == null)
- return null;
-
- // Desktop compat note: If there is more than one nested type that matches the name in a case-blind match,
- // we might not return the same one that the desktop returns. The actual selection method is influenced both by the type's
- // placement in the IL and the implementation details of the CLR's internal hashtables so it would be very
- // hard to replicate here.
- //
- // Desktop compat note #2: Case-insensitive lookups: If we don't find a match, we do *not* go back and search
- // other declaring types that might match the case-insensitive search and contain the nested type being sought.
- // Though this is somewhat unsatisfactory, the desktop CLR has the same limitation.
-
- // Don't change these flags - we may be talking to a third party type here and we need to invoke it the way CoreClr does.
- BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic;
- Type? nestedType;
- if (!getTypeOptions.IgnoreCase)
- {
- nestedType = declaringType.GetNestedType(_nestedTypeName, bf);
- }
- else
- {
- // Return the first name that matches. Which one gets returned on a multiple match is an implementation detail.
- // Unfortunately, compat prevents us from just throwing AmbiguousMatchException.
- nestedType = null;
- string lowerNestedTypeName = _nestedTypeName.ToLowerInvariant(); //@todo: Once String.Equals() works with StringComparison.InvariantIgnoreCase, it would be better to use that.
- foreach (Type nt in declaringType.GetNestedTypes(bf))
- {
- if (nt.Name.ToLowerInvariant() == lowerNestedTypeName)
- {
- nestedType = nt;
- break;
- }
- }
- }
- if (nestedType == null && getTypeOptions.ThrowOnError)
- throw Helpers.CreateTypeLoadException(ToString(), containingAssemblyIfAny);
- return nestedType;
- }
-
- private readonly string _nestedTypeName;
- private readonly NamedTypeName _declaringType;
- }
-
- //
- // Abstract base for array, byref and pointer type names.
- //
- internal abstract class HasElementTypeName : NonQualifiedTypeName
- {
- public HasElementTypeName(TypeName elementTypeName)
- {
- ElementTypeName = elementTypeName;
- }
-
- protected TypeName ElementTypeName { get; }
- }
-
- //
- // A single-dimensional zero-lower-bound array type name.
- //
- internal sealed class ArrayTypeName : HasElementTypeName
- {
- public ArrayTypeName(TypeName elementTypeName)
- : base(elementTypeName)
- {
- }
-
- public sealed override string ToString()
- {
- return ElementTypeName + "[]";
- }
-
- [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi",
- Justification = "Used to implement resolving types from strings.")]
- public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions)
- {
- return ElementTypeName.ResolveType(containingAssemblyIfAny, getTypeOptions)?.MakeArrayType();
- }
- }
-
- //
- // A multidim array type name.
- //
- internal sealed class MultiDimArrayTypeName : HasElementTypeName
- {
- public MultiDimArrayTypeName(TypeName elementTypeName, int rank)
- : base(elementTypeName)
- {
- _rank = rank;
- }
-
- public sealed override string ToString()
- {
- return ElementTypeName + "[" + (_rank == 1 ? "*" : new string(',', _rank - 1)) + "]";
- }
-
- [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi",
- Justification = "Used to implement resolving types from strings.")]
- public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions)
- {
- return ElementTypeName.ResolveType(containingAssemblyIfAny, getTypeOptions)?.MakeArrayType(_rank);
- }
-
- private readonly int _rank;
- }
-
- //
- // A byref type.
- //
- internal sealed class ByRefTypeName : HasElementTypeName
- {
- public ByRefTypeName(TypeName elementTypeName)
- : base(elementTypeName)
- {
- }
-
- public sealed override string ToString()
- {
- return ElementTypeName + "&";
- }
-
- public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions)
- {
- return ElementTypeName.ResolveType(containingAssemblyIfAny, getTypeOptions)?.MakeByRefType();
- }
- }
-
- //
- // A pointer type.
- //
- internal sealed class PointerTypeName : HasElementTypeName
- {
- public PointerTypeName(TypeName elementTypeName)
- : base(elementTypeName)
- {
- }
-
- public sealed override string ToString()
- {
- return ElementTypeName + "*";
- }
-
- public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions)
- {
- return ElementTypeName.ResolveType(containingAssemblyIfAny, getTypeOptions)?.MakePointerType();
- }
- }
-
- //
- // A constructed generic type.
- //
- internal sealed class ConstructedGenericTypeName : NonQualifiedTypeName
- {
- public ConstructedGenericTypeName(NamedTypeName genericTypeDefinition, IList genericTypeArguments)
- {
- _genericTypeDefinition = genericTypeDefinition;
- _genericTypeArguments = genericTypeArguments;
- }
-
- public sealed override string ToString()
- {
- string s = _genericTypeDefinition.ToString();
- s += "[";
- string sep = "";
- foreach (TypeName genericTypeArgument in _genericTypeArguments)
- {
- s += sep;
- sep = ",";
- if (genericTypeArgument is AssemblyQualifiedTypeName)
- s += "[" + genericTypeArgument.ToString() + "]";
- else
- s += genericTypeArgument.ToString();
- }
- s += "]";
- return s;
- }
-
- [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:RequiresUnreferencedCode",
- Justification = "Used to implement resolving types from strings.")]
- [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi",
- Justification = "Used to implement resolving types from strings.")]
- public sealed override Type ResolveType(Assembly containingAssemblyIfAny, GetTypeOptions getTypeOptions)
- {
- Type genericTypeDefinition = _genericTypeDefinition.ResolveType(containingAssemblyIfAny, getTypeOptions);
- if (genericTypeDefinition == null)
- return null;
-
- int numGenericArguments = _genericTypeArguments.Count;
- Type[] genericArgumentTypes = new Type[numGenericArguments];
- for (int i = 0; i < numGenericArguments; i++)
- {
- // Do not pass containingAssemblyIfAny down to ResolveType for the generic type arguments.
- if ((genericArgumentTypes[i] = _genericTypeArguments[i].ResolveType(null, getTypeOptions)) == null)
- return null;
- }
- return genericTypeDefinition.MakeGenericType(genericArgumentTypes);
- }
-
- private readonly NamedTypeName _genericTypeDefinition;
- private readonly IList _genericTypeArguments;
- }
-}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs
deleted file mode 100644
index d51157cc5b41d..0000000000000
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs
+++ /dev/null
@@ -1,222 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Diagnostics;
-using System.Collections.Generic;
-using System.Reflection.Runtime.Assemblies;
-
-namespace System.Reflection.Runtime.TypeParsing
-{
- //
- // Parser for type names passed to GetType() apis.
- //
- internal sealed class TypeParser
- {
- //
- // Parses a typename. The typename may be optionally postpended with a "," followed by a legal assembly name.
- //
- public static TypeName ParseAssemblyQualifiedTypeName(string s, bool throwOnError)
- {
- if (throwOnError)
- return ParseAssemblyQualifiedTypeName(s);
-
- try
- {
- return ParseAssemblyQualifiedTypeName(s);
- }
- catch (ArgumentException)
- {
- return null;
- }
- }
-
- //
- // Parses a typename. The typename may be optionally postpended with a "," followed by a legal assembly name.
- //
- private static TypeName ParseAssemblyQualifiedTypeName(string s)
- {
- // Desktop compat: a whitespace-only "typename" qualified by an assembly name throws an ArgumentException rather than
- // a TypeLoadException.
- int idx = 0;
- while (idx < s.Length && char.IsWhiteSpace(s[idx]))
- {
- idx++;
- }
- if (idx < s.Length && s[idx] == ',')
- throw new ArgumentException(SR.Arg_TypeLoadNullStr, $"typeName@{idx}");
-
- try
- {
- TypeParser parser = new TypeParser(s);
- NonQualifiedTypeName typeName = parser.ParseNonQualifiedTypeName();
- TokenType token = parser._lexer.GetNextToken();
- if (token == TokenType.End)
- return typeName;
- if (token == TokenType.Comma)
- {
- RuntimeAssemblyName assemblyName = parser._lexer.GetNextAssemblyName();
- token = parser._lexer.Peek;
- if (token != TokenType.End)
- throw new ArgumentException();
- return new AssemblyQualifiedTypeName(typeName, assemblyName);
- }
- throw new ArgumentException();
- }
- catch (TypeLexer.IllegalEscapeSequenceException)
- {
- // Emulates a CLR4.5 bug that causes any string that contains an illegal escape sequence to be parsed as the empty string.
- return ParseAssemblyQualifiedTypeName(string.Empty);
- }
- }
-
- private TypeParser(string s)
- {
- _lexer = new TypeLexer(s);
- }
-
-
- //
- // Parses a type name without any assembly name qualification.
- //
- private NonQualifiedTypeName ParseNonQualifiedTypeName()
- {
- // Parse the named type or constructed generic type part first.
- NonQualifiedTypeName typeName = ParseNamedOrConstructedGenericTypeName();
-
- // Iterate through any "has-element" qualifiers ([], &, *).
- for (;;)
- {
- TokenType token = _lexer.Peek;
- if (token == TokenType.End)
- break;
- if (token == TokenType.Asterisk)
- {
- _lexer.Skip();
- typeName = new PointerTypeName(typeName);
- }
- else if (token == TokenType.Ampersand)
- {
- _lexer.Skip();
- typeName = new ByRefTypeName(typeName);
- }
- else if (token == TokenType.OpenSqBracket)
- {
- _lexer.Skip();
- token = _lexer.GetNextToken();
- if (token == TokenType.Asterisk)
- {
- typeName = new MultiDimArrayTypeName(typeName, 1);
- token = _lexer.GetNextToken();
- }
- else
- {
- int rank = 1;
- while (token == TokenType.Comma)
- {
- token = _lexer.GetNextToken();
- rank++;
- }
- if (rank == 1)
- typeName = new ArrayTypeName(typeName);
- else
- typeName = new MultiDimArrayTypeName(typeName, rank);
- }
- if (token != TokenType.CloseSqBracket)
- throw new ArgumentException();
- }
- else
- {
- break;
- }
- }
- return typeName;
- }
-
- //
- // Foo or Foo+Inner or Foo[String] or Foo+Inner[String]
- //
- private NonQualifiedTypeName ParseNamedOrConstructedGenericTypeName()
- {
- NamedTypeName namedType = ParseNamedTypeName();
- // Because "[" is used both for generic arguments and array indexes, we must peek two characters deep.
- if (!(_lexer.Peek == TokenType.OpenSqBracket && (_lexer.PeekSecond == TokenType.Other || _lexer.PeekSecond == TokenType.OpenSqBracket)))
- return namedType;
- else
- {
- _lexer.Skip();
- LowLevelListWithIList genericTypeArguments = new LowLevelListWithIList();
- for (;;)
- {
- TypeName genericTypeArgument = ParseGenericTypeArgument();
- genericTypeArguments.Add(genericTypeArgument);
- TokenType token = _lexer.GetNextToken();
- if (token == TokenType.CloseSqBracket)
- break;
- if (token != TokenType.Comma)
- throw new ArgumentException();
- }
-
- return new ConstructedGenericTypeName(namedType, genericTypeArguments);
- }
- }
-
- //
- // Foo or Foo+Inner
- //
- private NamedTypeName ParseNamedTypeName()
- {
- NamedTypeName namedType = ParseNamespaceTypeName();
- while (_lexer.Peek == TokenType.Plus)
- {
- _lexer.Skip();
- string nestedTypeName = _lexer.GetNextIdentifier();
- namedType = new NestedTypeName(nestedTypeName, namedType);
- }
- return namedType;
- }
-
- //
- // Non-nested named type.
- //
- private NamespaceTypeName ParseNamespaceTypeName()
- {
- string fullName = _lexer.GetNextIdentifier();
- return new NamespaceTypeName(fullName);
- }
-
- //
- // Parse a generic argument. In particular, generic arguments can take the special form [,].
- //
- private TypeName ParseGenericTypeArgument()
- {
- TokenType token = _lexer.GetNextToken();
- if (token == TokenType.Other)
- {
- NonQualifiedTypeName nonQualifiedTypeName = ParseNonQualifiedTypeName();
- return nonQualifiedTypeName;
- }
- else if (token == TokenType.OpenSqBracket)
- {
- RuntimeAssemblyName? assemblyName = null;
- NonQualifiedTypeName typeName = ParseNonQualifiedTypeName();
- token = _lexer.GetNextToken();
- if (token == TokenType.Comma)
- {
- assemblyName = _lexer.GetNextEmbeddedAssemblyName();
- token = _lexer.GetNextToken();
- }
- if (token != TokenType.CloseSqBracket)
- throw new ArgumentException();
- if (assemblyName == null)
- return typeName;
- else
- return new AssemblyQualifiedTypeName(typeName, assemblyName);
- }
- else
- throw new ArgumentException();
- }
-
- private readonly TypeLexer _lexer;
- }
-}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/TypeNameParser.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/TypeNameParser.NativeAot.cs
new file mode 100644
index 0000000000000..ea3c7f89b7104
--- /dev/null
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/TypeNameParser.NativeAot.cs
@@ -0,0 +1,242 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Reflection.Runtime.Assemblies;
+using System.Reflection.Runtime.General;
+
+namespace System.Reflection
+{
+ //
+ // Parser for type names passed to GetType() apis.
+ //
+ internal ref partial struct TypeNameParser
+ {
+ private Func? _assemblyResolver;
+ private Func? _typeResolver;
+ private bool _throwOnError;
+ private bool _ignoreCase;
+ private bool _extensibleParser;
+ private Assembly? _topLevelAssembly;
+ private string? _defaultAssemblyName;
+
+ internal static Type? GetType(
+ string typeName,
+ bool throwOnError = false,
+ bool ignoreCase = false,
+ string? defaultAssemblyName = null)
+ {
+ return GetType(typeName, assemblyResolver: null, typeResolver: null,
+ throwOnError: throwOnError, ignoreCase: ignoreCase, extensibleParser: false, defaultAssemblyName: defaultAssemblyName);
+ }
+
+ internal static Type? GetType(
+ string typeName,
+ Func? assemblyResolver,
+ Func? typeResolver,
+ bool throwOnError = false,
+ bool ignoreCase = false,
+ bool extensibleParser = true,
+ string? defaultAssemblyName = null)
+ {
+ ArgumentNullException.ThrowIfNull(typeName);
+
+ // Compat: Empty name throws TypeLoadException instead of
+ // the natural ArgumentException
+ if (typeName.Length == 0)
+ {
+ if (throwOnError)
+ throw new TypeLoadException(SR.Arg_TypeLoadNullStr);
+ return null;
+ }
+
+ return new TypeNameParser(typeName)
+ {
+ _assemblyResolver = assemblyResolver,
+ _typeResolver = typeResolver,
+ _throwOnError = throwOnError,
+ _ignoreCase = ignoreCase,
+ _extensibleParser = extensibleParser,
+ _defaultAssemblyName = defaultAssemblyName
+ }.Parse();
+ }
+
+ internal static Type? GetType(
+ string typeName,
+ bool throwOnError,
+ bool ignoreCase,
+ Assembly topLevelAssembly)
+ {
+ return new TypeNameParser(typeName)
+ {
+ _throwOnError = throwOnError,
+ _ignoreCase = ignoreCase,
+ _topLevelAssembly = topLevelAssembly,
+ }.Parse();
+ }
+
+ private bool CheckTopLevelAssemblyQualifiedName()
+ {
+ if (_topLevelAssembly is not null)
+ {
+ if (_throwOnError)
+ throw new ArgumentException(SR.Argument_AssemblyGetTypeCannotSpecifyAssembly);
+ return false;
+ }
+ return true;
+ }
+
+ private Assembly? ResolveAssembly(string assemblyName)
+ {
+ Assembly? assembly;
+ if (_assemblyResolver is not null)
+ {
+ assembly = _assemblyResolver(new AssemblyName(assemblyName));
+ }
+ else
+ {
+ assembly = RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(RuntimeAssemblyName.Parse(assemblyName));
+ }
+
+ if (assembly is null && _throwOnError)
+ {
+ throw new FileNotFoundException(SR.Format(SR.FileNotFound_ResolveAssembly, assemblyName));
+ }
+
+ return assembly;
+ }
+
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
+ Justification = "GetType APIs are marked as RequiresUnreferencedCode.")]
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
+ Justification = "GetType APIs are marked as RequiresUnreferencedCode.")]
+ private Type? GetType(string typeName, ReadOnlySpan nestedTypeNames, string? assemblyNameIfAny)
+ {
+ Assembly? assembly;
+
+ if (assemblyNameIfAny is not null)
+ {
+ assembly = ResolveAssembly(assemblyNameIfAny);
+ if (assembly is null)
+ return null;
+ }
+ else
+ {
+ assembly = _topLevelAssembly;
+ }
+
+ Type? type = null;
+
+ // Resolve the top level type.
+ if (_typeResolver is not null)
+ {
+ string escapedTypeName = EscapeTypeName(typeName);
+
+ type = _typeResolver(assembly, escapedTypeName, _ignoreCase);
+
+ if (type is null)
+ {
+ if (_throwOnError)
+ {
+ throw new TypeLoadException(assembly is null ?
+ SR.Format(SR.TypeLoad_ResolveType, escapedTypeName) :
+ SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, escapedTypeName, assembly.FullName));
+ }
+ return null;
+ }
+ }
+ else
+ {
+ if (assembly is not null)
+ {
+ if (assembly is RuntimeAssemblyInfo runtimeAssembly)
+ {
+ type = runtimeAssembly.GetTypeCore(typeName, throwOnError: _throwOnError, ignoreCase: _ignoreCase);
+ }
+ else
+ {
+ // This is a third-party Assembly object. We can emulate GetTypeCore() by calling the public GetType()
+ // method. This is wasteful because it'll probably reparse a type string that we've already parsed
+ // but it can't be helped.
+ type = assembly.GetType(EscapeTypeName(typeName), throwOnError: _throwOnError, ignoreCase: _ignoreCase);
+ }
+
+ if (type is null)
+ return null;
+ }
+ else
+ {
+ RuntimeAssemblyInfo? defaultAssembly = null;
+ if (_defaultAssemblyName != null)
+ {
+ defaultAssembly = RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(RuntimeAssemblyName.Parse(_defaultAssemblyName));
+ if (defaultAssembly != null)
+ {
+ type = defaultAssembly.GetTypeCore(typeName, throwOnError: false, ignoreCase: _ignoreCase);
+ }
+ }
+
+ RuntimeAssemblyInfo? coreLib = null;
+ if (type is null)
+ {
+ coreLib = (RuntimeAssemblyInfo)typeof(object).Assembly;
+ if (coreLib != assembly)
+ {
+ type = coreLib.GetTypeCore(typeName, throwOnError: false, ignoreCase: _ignoreCase);
+ }
+ }
+
+ if (type is null)
+ {
+ if (_throwOnError)
+ {
+ throw Helpers.CreateTypeLoadException(typeName, (defaultAssembly ?? coreLib).FullName);
+ }
+ return null;
+ }
+ }
+ }
+
+ for (int i = 0; i < nestedTypeNames.Length; i++)
+ {
+ BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public;
+ if (_ignoreCase && _extensibleParser)
+ bindingFlags |= BindingFlags.IgnoreCase;
+
+ Type declaringType = type;
+
+ type = type.GetNestedType(nestedTypeNames[i], bindingFlags);
+
+ // Compat: Non-extensible parser allows ambiguous matches with ignore case lookup
+ if (type is null && _ignoreCase && !_extensibleParser)
+ {
+ // Return the first name that matches. Which one gets returned on a multiple match is an implementation detail.
+ string lowerName = nestedTypeNames[i].ToLowerInvariant();
+ foreach (Type nt in declaringType.GetNestedTypes(BindingFlags.NonPublic | BindingFlags.Public))
+ {
+ if (nt.Name.ToLowerInvariant() == lowerName)
+ {
+ type = nt;
+ break;
+ }
+ }
+ }
+
+ if (type is null)
+ {
+ if (_throwOnError)
+ {
+ throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveNestedType,
+ nestedTypeNames[i], (i > 0) ? nestedTypeNames[i - 1] : typeName));
+ }
+ return null;
+ }
+ }
+
+ return type;
+ }
+ }
+}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.NativeAot.cs
index 617858d451794..d7475aa13fdd6 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.NativeAot.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.NativeAot.cs
@@ -71,7 +71,10 @@ private static Type GetTypeFromEETypePtrSlow(EETypePtr eeType, ref GCHandle hand
public static Type GetType(string typeName, bool throwOnError) => GetType(typeName, throwOnError: throwOnError, ignoreCase: false);
[Intrinsic]
[RequiresUnreferencedCode("The type might be removed")]
- public static Type GetType(string typeName, bool throwOnError, bool ignoreCase) => GetType(typeName, null, null, throwOnError: throwOnError, ignoreCase: ignoreCase);
+ public static Type GetType(string typeName, bool throwOnError, bool ignoreCase)
+ {
+ return TypeNameParser.GetType(typeName, throwOnError: throwOnError, ignoreCase: ignoreCase);
+ }
[Intrinsic]
[RequiresUnreferencedCode("The type might be removed")]
@@ -81,6 +84,9 @@ private static Type GetTypeFromEETypePtrSlow(EETypePtr eeType, ref GCHandle hand
public static Type GetType(string typeName, Func? assemblyResolver, Func? typeResolver, bool throwOnError) => GetType(typeName, assemblyResolver, typeResolver, throwOnError: throwOnError, ignoreCase: false);
[Intrinsic]
[RequiresUnreferencedCode("The type might be removed")]
- public static Type GetType(string typeName, Func? assemblyResolver, Func? typeResolver, bool throwOnError, bool ignoreCase) => RuntimeAugments.Callbacks.GetType(typeName, assemblyResolver, typeResolver, throwOnError: throwOnError, ignoreCase: ignoreCase, defaultAssembly: null);
+ public static Type GetType(string typeName, Func? assemblyResolver, Func? typeResolver, bool throwOnError, bool ignoreCase)
+ {
+ return TypeNameParser.GetType(typeName, assemblyResolver, typeResolver, throwOnError: throwOnError, ignoreCase: ignoreCase);
+ }
}
}
diff --git a/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionExecutionDomainCallbacksImplementation.cs b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionExecutionDomainCallbacksImplementation.cs
index 54f0931a618ba..6f3e084711d6b 100644
--- a/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionExecutionDomainCallbacksImplementation.cs
+++ b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionExecutionDomainCallbacksImplementation.cs
@@ -22,7 +22,6 @@ internal class ReflectionExecutionDomainCallbacksImplementation : ReflectionExec
public override MethodBase GetMethodBaseFromStartAddressIfAvailable(IntPtr methodStartAddress) => null;
public override Type GetNamedTypeForHandle(RuntimeTypeHandle typeHandle, bool isGenericTypeDefinition) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle);
public override Type GetPointerTypeForHandle(RuntimeTypeHandle typeHandle) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle);
- public override Type GetType(string typeName, Func assemblyResolver, Func? typeResolver, bool throwOnError, bool ignoreCase, string defaultAssembly) => throw new NotSupportedException(SR.Reflection_Disabled);
public override RuntimeTypeHandle GetTypeHandleIfAvailable(Type type) => type.TypeHandle;
public override bool IsReflectionBlocked(RuntimeTypeHandle typeHandle) => false;
public override bool SupportsReflection(Type type) => false;
diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs
index 9ac138adf345d..cc3b1d8d59549 100644
--- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs
+++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecution.cs
@@ -59,27 +59,6 @@ internal static void Initialize()
ExecutionEnvironment = executionEnvironment;
}
- //
- // This entry is targeted by the ILTransformer to implement Type.GetType()'s ability to detect the calling assembly and use it as
- // a default assembly name.
- //
- public static Type GetType(string typeName, string callingAssemblyName, bool throwOnError, bool ignoreCase)
- {
- return ExtensibleGetType(typeName, callingAssemblyName, null, null, throwOnError: throwOnError, ignoreCase: ignoreCase);
- }
-
- //
- // This entry is targeted by the ILTransformer to implement Type.GetType()'s ability to detect the calling assembly and use it as
- // a default assembly name.
- //
- public static Type ExtensibleGetType(string typeName, string callingAssemblyName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase)
- {
- LowLevelListWithIList defaultAssemblies = new LowLevelListWithIList();
- defaultAssemblies.Add(callingAssemblyName);
- defaultAssemblies.Add(AssemblyBinder.DefaultAssemblyNameForGetType);
- return ReflectionCoreExecution.ExecutionDomain.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, defaultAssemblies);
- }
-
public static bool TryGetMethodMetadataFromStartAddress(IntPtr methodStartAddress, out MetadataReader reader, out TypeDefinitionHandle typeHandle, out MethodHandle methodHandle)
{
reader = null;
diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs
index bea0d44a506cc..72bd9a41c47e2 100644
--- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs
+++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs
@@ -31,15 +31,6 @@ public ReflectionExecutionDomainCallbacksImplementation(ExecutionDomain executio
_executionEnvironment = executionEnvironment;
}
- public sealed override Type GetType(string typeName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase, string defaultAssemblyName)
- {
- LowLevelListWithIList defaultAssemblies = new LowLevelListWithIList();
- if (defaultAssemblyName != null)
- defaultAssemblies.Add(defaultAssemblyName);
- defaultAssemblies.Add(AssemblyBinder.DefaultAssemblyNameForGetType);
- return _executionDomain.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, defaultAssemblies);
- }
-
public sealed override bool IsReflectionBlocked(RuntimeTypeHandle typeHandle)
{
return _executionEnvironment.IsReflectionBlocked(typeHandle);
diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp
index 85ca5a27f6981..455cb0f8d9204 100644
--- a/src/coreclr/vm/appdomain.cpp
+++ b/src/coreclr/vm/appdomain.cpp
@@ -1589,6 +1589,9 @@ Module* SystemDomain::GetCallersModule(StackCrawlMark* stackMark)
}
CONTRACTL_END;
+ if (stackMark == NULL)
+ return NULL;
+
GCX_COOP();
CallersDataWithStackMark cdata;
diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp
index 526dbe4f61703..eac1381922c53 100644
--- a/src/coreclr/vm/assemblynative.cpp
+++ b/src/coreclr/vm/assemblynative.cpp
@@ -335,51 +335,119 @@ extern "C" void QCALLTYPE AssemblyNative_GetLocation(QCall::AssemblyHandle pAsse
END_QCALL;
}
-extern "C" void QCALLTYPE AssemblyNative_GetType(QCall::AssemblyHandle pAssembly,
- LPCWSTR wszName,
- BOOL bThrowOnError,
- BOOL bIgnoreCase,
- QCall::ObjectHandleOnStack retType,
- QCall::ObjectHandleOnStack keepAlive,
- QCall::ObjectHandleOnStack pAssemblyLoadContext)
+extern "C" void QCALLTYPE AssemblyNative_GetTypeCore(QCall::AssemblyHandle assemblyHandle,
+ LPCSTR szTypeName,
+ LPCSTR * rgszNestedTypeNames,
+ int32_t cNestedTypeNamesLength,
+ QCall::ObjectHandleOnStack retType)
{
CONTRACTL
{
QCALL_CHECK;
- PRECONDITION(CheckPointer(wszName));
+ PRECONDITION(CheckPointer(szTypeName));
}
CONTRACTL_END;
- TypeHandle retTypeHandle;
-
BEGIN_QCALL;
- BOOL prohibitAsmQualifiedName = TRUE;
+ Assembly* pAssembly = assemblyHandle->GetAssembly();
+
+ TypeHandle th = TypeHandle();
+ Module* pManifestModule = pAssembly->GetModule();
+ ClassLoader* pClassLoader = pAssembly->GetLoader();
+
+ NameHandle typeName(pManifestModule, mdtBaseType);
+
+ for (int32_t i = -1; i < cNestedTypeNamesLength; i++)
+ {
+ typeName.SetName((i == -1) ? szTypeName : rgszNestedTypeNames[i]);
- AssemblyBinder * pBinder = NULL;
+ // typeName.m_pBucket gets set here if the type is found
+ // it will be used in the next iteration to look up the nested type
+ th = pClassLoader->LoadTypeHandleThrowing(&typeName, CLASS_LOADED);
+
+ // If we didn't find a type, don't bother looking for its nested type
+ if (th.IsNull())
+ break;
+
+ if (th.GetAssembly() != pAssembly)
+ {
+ // For forwarded type, use the found assembly class loader for potential nested types search
+ // The nested type has to be in the same module as the nesting type, so it doesn't make
+ // sense to follow the same chain of type forwarders again for the nested type
+ pClassLoader = th.GetAssembly()->GetLoader();
+ }
+ }
- if (*pAssemblyLoadContext.m_ppObject != NULL)
+ if (!th.IsNull())
{
GCX_COOP();
- ASSEMBLYLOADCONTEXTREF * pAssemblyLoadContextRef = reinterpret_cast(pAssemblyLoadContext.m_ppObject);
+ retType.Set(th.GetManagedClassObject());
+ }
- INT_PTR nativeAssemblyBinder = (*pAssemblyLoadContextRef)->GetNativeAssemblyBinder();
+ END_QCALL;
+}
- pBinder = reinterpret_cast(nativeAssemblyBinder);
+extern "C" void QCALLTYPE AssemblyNative_GetTypeCoreIgnoreCase(QCall::AssemblyHandle assemblyHandle,
+ LPCWSTR wszTypeName,
+ LPCWSTR* rgwszNestedTypeNames,
+ int32_t cNestedTypeNamesLength,
+ QCall::ObjectHandleOnStack retType)
+{
+ CONTRACTL
+ {
+ QCALL_CHECK;
+ PRECONDITION(CheckPointer(wszTypeName));
}
+ CONTRACTL_END;
- // Load the class from this assembly (fail if it is in a different one).
- retTypeHandle = TypeName::GetTypeManaged(wszName, pAssembly, bThrowOnError, bIgnoreCase, prohibitAsmQualifiedName, pAssembly->GetAssembly(), (OBJECTREF*)keepAlive.m_ppObject, pBinder);
+ BEGIN_QCALL;
+
+ Assembly* pAssembly = assemblyHandle->GetAssembly();
+
+ TypeHandle th = TypeHandle();
+ Module* pManifestModule = pAssembly->GetModule();
+ ClassLoader* pClassLoader = pAssembly->GetLoader();
+
+ NameHandle typeName(pManifestModule, mdtBaseType);
+
+ // Set up the name handle
+ typeName.SetCaseInsensitive();
- if (!retTypeHandle.IsNull())
+ for (int32_t i = -1; i < cNestedTypeNamesLength; i++)
+ {
+ // each extra name represents one more level of nesting
+ StackSString name((i == -1) ? wszTypeName : rgwszNestedTypeNames[i]);
+
+ // The type name is expected to be lower-cased by the caller for case-insensitive lookups
+ name.LowerCase();
+
+ typeName.SetName(name.GetUTF8());
+
+ // typeName.m_pBucket gets set here if the type is found
+ // it will be used in the next iteration to look up the nested type
+ th = pClassLoader->LoadTypeHandleThrowing(&typeName, CLASS_LOADED);
+
+ // If we didn't find a type, don't bother looking for its nested type
+ if (th.IsNull())
+ break;
+
+ if (th.GetAssembly() != pAssembly)
+ {
+ // For forwarded type, use the found assembly class loader for potential nested types search
+ // The nested type has to be in the same module as the nesting type, so it doesn't make
+ // sense to follow the same chain of type forwarders again for the nested type
+ pClassLoader = th.GetAssembly()->GetLoader();
+ }
+ }
+
+ if (!th.IsNull())
{
GCX_COOP();
- retType.Set(retTypeHandle.GetManagedClassObject());
+ retType.Set(th.GetManagedClassObject());
}
END_QCALL;
-
- return;
}
extern "C" void QCALLTYPE AssemblyNative_GetForwardedType(QCall::AssemblyHandle pAssembly, mdToken mdtExternalType, QCall::ObjectHandleOnStack retType)
diff --git a/src/coreclr/vm/assemblynative.hpp b/src/coreclr/vm/assemblynative.hpp
index 944c327fb6693..5c08c7a8edb31 100644
--- a/src/coreclr/vm/assemblynative.hpp
+++ b/src/coreclr/vm/assemblynative.hpp
@@ -80,7 +80,10 @@ extern "C" BYTE * QCALLTYPE AssemblyNative_GetResource(QCall::AssemblyHandle pAs
extern "C" void QCALLTYPE AssemblyNative_GetVersion(QCall::AssemblyHandle pAssembly, INT32* pMajorVersion, INT32* pMinorVersion, INT32*pBuildNumber, INT32* pRevisionNumber);
-extern "C" void QCALLTYPE AssemblyNative_GetType(QCall::AssemblyHandle pAssembly, LPCWSTR wszName, BOOL bThrowOnError, BOOL bIgnoreCase, QCall::ObjectHandleOnStack retType, QCall::ObjectHandleOnStack keepAlive, QCall::ObjectHandleOnStack pAssemblyLoadContext);
+extern "C" void QCALLTYPE AssemblyNative_GetTypeCore(QCall::AssemblyHandle pAssembly, LPCSTR szTypeName, LPCSTR* rgszNestedTypeNames, int32_t cNestedTypeNamesLength, QCall::ObjectHandleOnStack retType);
+
+
+extern "C" void QCALLTYPE AssemblyNative_GetTypeCoreIgnoreCase(QCall::AssemblyHandle pAssembly, LPCWSTR wszTypeName, LPCWSTR* rgwszNestedTypeNames, int32_t cNestedTypeNamesLength, QCall::ObjectHandleOnStack retType);
extern "C" void QCALLTYPE AssemblyNative_GetForwardedType(QCall::AssemblyHandle pAssembly, mdToken mdtExternalType, QCall::ObjectHandleOnStack retType);
diff --git a/src/coreclr/vm/commodule.cpp b/src/coreclr/vm/commodule.cpp
index eeaa33d55091d..46c67679c265d 100644
--- a/src/coreclr/vm/commodule.cpp
+++ b/src/coreclr/vm/commodule.cpp
@@ -592,52 +592,6 @@ extern "C" mdTypeSpec QCALLTYPE ModuleBuilder_GetTokenFromTypeSpec(QCall::Module
}
-// GetType
-// Given a class name, this method will look for that class
-// with in the module.
-extern "C" void QCALLTYPE RuntimeModule_GetType(QCall::ModuleHandle pModule, LPCWSTR wszName, BOOL bThrowOnError, BOOL bIgnoreCase, QCall::ObjectHandleOnStack retType, QCall::ObjectHandleOnStack keepAlive)
-{
- CONTRACTL
- {
- QCALL_CHECK;
- PRECONDITION(CheckPointer(wszName));
- }
- CONTRACTL_END;
-
- TypeHandle retTypeHandle;
-
- BEGIN_QCALL;
-
- DomainAssembly *pAssembly = pModule->GetDomainAssembly();
- _ASSERTE(pAssembly);
-
- BOOL prohibitAsmQualifiedName = TRUE;
-
- // Load the class from this assembly (fail if it is in a different one).
- retTypeHandle = TypeName::GetTypeManaged(wszName, pAssembly, bThrowOnError, bIgnoreCase, prohibitAsmQualifiedName, NULL, (OBJECTREF*)keepAlive.m_ppObject);
-
- // Verify that it's in 'this' module
- // But, if it's in a different assembly than expected, that's okay, because
- // it just means that it's been type forwarded.
- if (!retTypeHandle.IsNull())
- {
- if ( (retTypeHandle.GetModule() != pModule) &&
- (retTypeHandle.GetModule()->GetAssembly() == pModule->GetAssembly()) )
- retTypeHandle = TypeHandle();
- }
-
- if (!retTypeHandle.IsNull())
- {
- GCX_COOP();
- retType.Set(retTypeHandle.GetManagedClassObject());
- }
-
- END_QCALL;
-
- return;
-}
-
-
// GetName
// This routine will return the name of the module as a String
extern "C" void QCALLTYPE RuntimeModule_GetScopeName(QCall::ModuleHandle pModule, QCall::StringHandleOnStack retString)
diff --git a/src/coreclr/vm/commodule.h b/src/coreclr/vm/commodule.h
index e5f2a5a94aa17..ecd1681a8deb5 100644
--- a/src/coreclr/vm/commodule.h
+++ b/src/coreclr/vm/commodule.h
@@ -63,11 +63,6 @@ extern "C" INT32 QCALLTYPE ModuleBuilder_GetMemberRefFromSignature(QCall::Module
// GetTokenFromTypeSpec
extern "C" mdTypeSpec QCALLTYPE ModuleBuilder_GetTokenFromTypeSpec(QCall::ModuleHandle pModule, LPCBYTE pSignature, INT32 sigLength);
-// GetType
-// Given a class type, this method will look for that type
-// with in the module.
-extern "C" void QCALLTYPE RuntimeModule_GetType(QCall::ModuleHandle pModule, LPCWSTR wszName, BOOL bThrowOnError, BOOL bIgnoreCase, QCall::ObjectHandleOnStack retType, QCall::ObjectHandleOnStack keepAlive);
-
// GetStringConstant
// If this is a dynamic module, this routine will define a new
// string constant or return the token of an existing constant.
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 29f3d448ce5f7..dbaebb2e0b3d5 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -788,10 +788,6 @@ DEFINE_METHOD(SAFE_HANDLE, RELEASE_HANDLE, ReleaseHandle,
DEFINE_METHOD(SAFE_HANDLE, DISPOSE, Dispose, IM_RetVoid)
DEFINE_METHOD(SAFE_HANDLE, DISPOSE_BOOL, Dispose, IM_Bool_RetVoid)
-
-DEFINE_CLASS(SAFE_TYPENAMEPARSER_HANDLE, System, SafeTypeNameParserHandle)
-DEFINE_METHOD(SAFE_TYPENAMEPARSER_HANDLE, CTOR, .ctor, IM_RetVoid)
-
DEFINE_CLASS(SECURITY_EXCEPTION, Security, SecurityException)
DEFINE_CLASS_U(Diagnostics, StackFrameHelper, StackFrameHelper)
diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp
index 7c0a89d26483d..3cae48e72eb5a 100644
--- a/src/coreclr/vm/qcallentrypoints.cpp
+++ b/src/coreclr/vm/qcallentrypoints.cpp
@@ -111,7 +111,6 @@ static const Entry s_QCall[] =
DllImportEntry(RuntimeMethodHandle_StripMethodInstantiation)
DllImportEntry(RuntimeMethodHandle_IsCAVisibleFromDecoratedType)
DllImportEntry(RuntimeMethodHandle_Destroy)
- DllImportEntry(RuntimeModule_GetType)
DllImportEntry(RuntimeModule_GetScopeName)
DllImportEntry(RuntimeModule_GetFullyQualifiedName)
DllImportEntry(StackFrame_GetMethodDescFromNativeIP)
@@ -151,12 +150,6 @@ static const Entry s_QCall[] =
DllImportEntry(TypeBuilder_SetConstantValue)
DllImportEntry(TypeBuilder_DefineCustomAttribute)
DllImportEntry(MdUtf8String_EqualsCaseInsensitive)
- DllImportEntry(TypeName_ReleaseTypeNameParser)
- DllImportEntry(TypeName_CreateTypeNameParser)
- DllImportEntry(TypeName_GetNames)
- DllImportEntry(TypeName_GetTypeArguments)
- DllImportEntry(TypeName_GetModifiers)
- DllImportEntry(TypeName_GetAssemblyName)
DllImportEntry(Array_GetElementConstructorEntrypoint)
DllImportEntry(AssemblyName_InitializeAssemblySpec)
DllImportEntry(AssemblyNative_GetFullName)
@@ -170,7 +163,8 @@ static const Entry s_QCall[] =
DllImportEntry(AssemblyNative_GetSimpleName)
DllImportEntry(AssemblyNative_GetVersion)
DllImportEntry(AssemblyNative_InternalLoad)
- DllImportEntry(AssemblyNative_GetType)
+ DllImportEntry(AssemblyNative_GetTypeCore)
+ DllImportEntry(AssemblyNative_GetTypeCoreIgnoreCase)
DllImportEntry(AssemblyNative_GetForwardedType)
DllImportEntry(AssemblyNative_GetManifestResourceInfo)
DllImportEntry(AssemblyNative_GetModules)
diff --git a/src/coreclr/vm/typeparse.cpp b/src/coreclr/vm/typeparse.cpp
index 04dde98b5f66d..3e06633a65f44 100644
--- a/src/coreclr/vm/typeparse.cpp
+++ b/src/coreclr/vm/typeparse.cpp
@@ -76,238 +76,6 @@ TypeName::~TypeName()
m_genericArguments[i]->Release();
}
-SAFEHANDLE TypeName::GetSafeHandle()
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_COOPERATIVE;
- }
- CONTRACTL_END;
-
- SAFEHANDLE objSafeHandle = NULL;
-
- GCPROTECT_BEGIN(objSafeHandle);
-
- objSafeHandle = (SAFEHANDLE)AllocateObject(CoreLibBinder::GetClass(CLASS__SAFE_TYPENAMEPARSER_HANDLE));
-
- MethodDescCallSite strCtor(METHOD__SAFE_TYPENAMEPARSER_HANDLE__CTOR);
-
- ARG_SLOT args[1] =
- {
- ObjToArgSlot(objSafeHandle)
- };
-
- strCtor.Call(args);
-
- this->AddRef();
- objSafeHandle->SetHandle(this);
-
- GCPROTECT_END();
-
- return objSafeHandle;
-}
-
-/*static*/
-extern "C" void QCALLTYPE TypeName_CreateTypeNameParser(LPCWSTR wszTypeName, QCall::ObjectHandleOnStack pHandle, BOOL throwOnError)
-{
- QCALL_CONTRACT;
-
- BEGIN_QCALL;
-
- DWORD error = (DWORD)-1;
- ReleaseHolder pTypeName = new TypeName(wszTypeName, &error);
- pTypeName->AddRef();
-
- if (error == (DWORD)-1)
- {
- GCX_COOP();
- pHandle.Set(pTypeName->GetSafeHandle());
- }
- else
- {
- if (throwOnError)
- {
- StackSString buf;
- StackSString msg(W("typeName@"));
- COUNT_T size = buf.GetUnicodeAllocation();
- _itow_s(error, buf.OpenUnicodeBuffer(size), size, /*radix*/10);
- buf.CloseBuffer();
- msg.Append(buf);
- COMPlusThrowArgumentException(msg.GetUnicode(), NULL);
- }
- }
-
- END_QCALL;
-}
-
-/*static*/
-extern "C" void QCALLTYPE TypeName_ReleaseTypeNameParser(TypeName * pTypeName)
-{
- CONTRACTL
- {
- QCALL_CHECK;
- PRECONDITION(CheckPointer(pTypeName));
- }
- CONTRACTL_END;
-
- BEGIN_QCALL;
-
- pTypeName->Release();
-
- END_QCALL;
-}
-
-/*static*/
-extern "C" void QCALLTYPE TypeName_GetNames(TypeName * pTypeName, QCall::ObjectHandleOnStack pNames)
-{
- CONTRACTL
- {
- QCALL_CHECK;
- PRECONDITION(CheckPointer(pTypeName));
- }
- CONTRACTL_END;
-
- BEGIN_QCALL;
-
- SArray names = pTypeName->GetNames();
- COUNT_T count = names.GetCount();
-
- GCX_COOP();
-
- if (count > 0)
- {
- PTRARRAYREF pReturnNames = NULL;
-
- GCPROTECT_BEGIN(pReturnNames);
-
- pReturnNames = (PTRARRAYREF)AllocateObjectArray(count, g_pStringClass);
-
- for (COUNT_T i = 0; i < count; i++)
- {
- STRINGREF str = StringObject::NewString(names[i]->GetUnicode());
- pReturnNames->SetAt(i, str);
- }
-
- pNames.Set(pReturnNames);
-
- GCPROTECT_END();
- }
- else
- {
- pNames.Set(NULL);
- }
-
- END_QCALL;
-}
-
-/*static*/
-extern "C" void QCALLTYPE TypeName_GetTypeArguments(TypeName * pTypeName, QCall::ObjectHandleOnStack pTypeArguments)
-{
- CONTRACTL
- {
- QCALL_CHECK;
- PRECONDITION(CheckPointer(pTypeName));
- }
- CONTRACTL_END;
-
- BEGIN_QCALL;
-
- SArray arguments = pTypeName->GetGenericArguments();
- COUNT_T count = arguments.GetCount();
-
- GCX_COOP();
-
- if (count > 0)
- {
- PTRARRAYREF pReturnArguments = NULL;
-
- GCPROTECT_BEGIN(pReturnArguments);
-
- pReturnArguments = (PTRARRAYREF)AllocateObjectArray(count, CoreLibBinder::GetClass(CLASS__SAFE_TYPENAMEPARSER_HANDLE));
-
- for (COUNT_T i = 0; i < count; i++)
- {
- SAFEHANDLE handle = arguments[i]->GetSafeHandle();
- _ASSERTE(handle != NULL);
-
- pReturnArguments->SetAt(i, handle);
- }
-
- pTypeArguments.Set(pReturnArguments);
-
- GCPROTECT_END();
- }
- else
- {
- pTypeArguments.Set(NULL);
- }
-
- END_QCALL;
-}
-
-/*static*/
-extern "C" void QCALLTYPE TypeName_GetModifiers(TypeName * pTypeName, QCall::ObjectHandleOnStack pModifiers)
-{
- CONTRACTL
- {
- QCALL_CHECK;
- PRECONDITION(CheckPointer(pTypeName));
- }
- CONTRACTL_END;
-
- BEGIN_QCALL;
-
- SArray modifiers = pTypeName->GetSignature();
- COUNT_T count = modifiers.GetCount();
-
- GCX_COOP();
-
- if (count > 0)
- {
- I4ARRAYREF pReturnModifiers = NULL;
-
- GCPROTECT_BEGIN(pReturnModifiers);
-
- //TODO: how do we Get
- pReturnModifiers = (I4ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_I4, count);
- INT32 *pToArray = pReturnModifiers->GetDirectPointerToNonObjectElements();
-
- for (COUNT_T i = 0; i < count; i++)
- {
- pToArray[i] = modifiers[i];
- }
-
- pModifiers.Set(pReturnModifiers);
-
- GCPROTECT_END();
- }
- else
- {
- pModifiers.Set(NULL);
- }
-
- END_QCALL;
-}
-
-/*static*/
-extern "C" void QCALLTYPE TypeName_GetAssemblyName(TypeName * pTypeName, QCall::StringHandleOnStack pAssemblyName)
-{
- CONTRACTL
- {
- QCALL_CHECK;
- PRECONDITION(CheckPointer(pTypeName));
- }
- CONTRACTL_END;
-
- BEGIN_QCALL;
-
- pAssemblyName.Set(*(pTypeName->GetAssembly()));
-
- END_QCALL;
-}
-
//
// TypeName::TypeNameParser
//
@@ -1261,12 +1029,11 @@ TypeHandle TypeName::GetTypeFromAsm()
//----------------------------------------------------------------------------------------------------------------
/* private */
TypeHandle
-TypeName::GetTypeHaveAssemblyHelper(
+TypeName::GetTypeHaveAssembly(
Assembly * pAssembly,
BOOL bThrowIfNotFound,
BOOL bIgnoreCase,
- OBJECTREF * pKeepAlive,
- BOOL bRecurse)
+ OBJECTREF * pKeepAlive)
{
WRAPPER_NO_CONTRACT;
@@ -1308,66 +1075,17 @@ TypeName::GetTypeHaveAssemblyHelper(
// it will be used in the next iteration to look up the nested type
th = pClassLoader->LoadTypeHandleThrowing(&typeName, CLASS_LOADED, pLookOnlyInModule);
- // DDB 117395: if we didn't find a type, don't bother looking for its nested type
+ // If we didn't find a type, don't bother looking for its nested type
if (th.IsNull())
break;
if (th.GetAssembly() != pAssembly)
- { // It is forwarded type
-
- // Use the found assembly class loader for potential nested types search
+ {
+ // For forwarded type, use the found assembly class loader for potential nested types search
// The nested type has to be in the same module as the nesting type, so it doesn't make
// sense to follow the same chain of type forwarders again for the nested type
pClassLoader = th.GetAssembly()->GetLoader();
}
-
- // Nested types must live in the module of the nesting type
- if ((i == 0) && (names.GetCount() > 1) && (pLookOnlyInModule == NULL))
- {
- Module * pFoundModule = th.GetModule();
-
- // Ensure that the bucket in the NameHandle is set to a valid bucket for all cases.
-
- // If the type is in the manifest module, it will always be set correctly,
- // or if the type is forwarded always lookup via the standard logic
- if ((pFoundModule == pManifestModule) || (pFoundModule->GetAssembly() != pAssembly))
- continue;
-
- pLookOnlyInModule = pFoundModule;
-
- // If the type is not in the manifest module, and the nesting type is in the exported
- // types table of the manifest module, but the nested type is not, then unless the bucket
- // is from the actual defining module, then the LoadTypeHandleThrowing logic will fail.
- // To fix this, we must force the loader to record the bucket that refers to the nesting type
- // from within the defining module's available class table.
-
- // Re-run the LoadTypeHandleThrowing, but force it to only look in the class table for the module which
- // defines the type. This should cause typeName.m_pBucket to be set to the bucket
- // which corresponds to the type in the defining module, instead of potentially in the manifest module.
- i = -1;
- typeName.SetBucket(HashedTypeEntry());
- }
- }
-
- if (th.IsNull() && bRecurse)
- {
- IMDInternalImport * pManifestImport = pManifestModule->GetMDImport();
- HENUMInternalHolder phEnum(pManifestImport);
- phEnum.EnumInit(mdtFile, mdTokenNil);
- mdToken mdFile;
-
- while (pManifestImport->EnumNext(&phEnum, &mdFile))
- {
- if (pManifestModule->LookupFile(mdFile))
- continue;
-
- pManifestModule->LoadModule(mdFile);
-
- th = GetTypeHaveAssemblyHelper(pAssembly, bThrowIfNotFound, bIgnoreCase, NULL, FALSE);
-
- if (!th.IsNull())
- break;
- }
}
}
EX_CATCH
diff --git a/src/coreclr/vm/typeparse.h b/src/coreclr/vm/typeparse.h
index 8491e0deeb992..32fac3b28a4bc 100644
--- a/src/coreclr/vm/typeparse.h
+++ b/src/coreclr/vm/typeparse.h
@@ -323,7 +323,6 @@ class TypeName
SArray& GetNames() { WRAPPER_NO_CONTRACT; return m_names; }
SArray& GetGenericArguments() { WRAPPER_NO_CONTRACT; return m_genericArguments; }
SArray& GetSignature() { WRAPPER_NO_CONTRACT; return m_signature; }
- SAFEHANDLE GetSafeHandle();
private:
TypeName() : m_bIsGenericArgument(FALSE), m_count(0) { LIMITED_METHOD_CONTRACT; }
@@ -385,11 +384,7 @@ class TypeName
//----------------------------------------------------------------------------------------------------------------
// These functions are the ones that actually loads the type once we've pinned down the Assembly it's in.
//----------------------------------------------------------------------------------------------------------------
- TypeHandle GetTypeHaveAssembly(Assembly* pAssembly, BOOL bThrowIfNotFound, BOOL bIgnoreCase, OBJECTREF *pKeepAlive)
- {
- return GetTypeHaveAssemblyHelper(pAssembly, bThrowIfNotFound, bIgnoreCase, pKeepAlive, TRUE);
- }
- TypeHandle GetTypeHaveAssemblyHelper(Assembly* pAssembly, BOOL bThrowIfNotFound, BOOL bIgnoreCase, OBJECTREF *pKeepAlive, BOOL bRecurse);
+ TypeHandle GetTypeHaveAssembly(Assembly* pAssembly, BOOL bThrowIfNotFound, BOOL bIgnoreCase, OBJECTREF* pKeepAlive);
private:
BOOL m_bIsGenericArgument;
@@ -401,11 +396,4 @@ class TypeName
Factory > m_nestNameFactory;
};
-extern "C" void QCALLTYPE TypeName_CreateTypeNameParser (LPCWSTR wszTypeName, QCall::ObjectHandleOnStack pNames, BOOL throwOnError);
-extern "C" void QCALLTYPE TypeName_ReleaseTypeNameParser(TypeName * pTypeName);
-extern "C" void QCALLTYPE TypeName_GetNames (TypeName * pTypeName, QCall::ObjectHandleOnStack pNames);
-extern "C" void QCALLTYPE TypeName_GetTypeArguments (TypeName * pTypeName, QCall::ObjectHandleOnStack pTypeArguments);
-extern "C" void QCALLTYPE TypeName_GetModifiers (TypeName * pTypeName, QCall::ObjectHandleOnStack pModifiers);
-extern "C" void QCALLTYPE TypeName_GetAssemblyName (TypeName * pTypeName, QCall::StringHandleOnStack pAssemblyName);
-
#endif
diff --git a/src/libraries/Common/src/System/Reflection/TypeNameParser.cs b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs
new file mode 100644
index 0000000000000..9dcdf11079d8d
--- /dev/null
+++ b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs
@@ -0,0 +1,631 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace System.Reflection
+{
+ //
+ // Parser for type names passed to GetType() apis.
+ //
+ [StructLayout(LayoutKind.Auto)]
+ internal ref partial struct TypeNameParser
+ {
+ private ReadOnlySpan _input;
+ private int _index;
+ private int _errorIndex; // Position for error reporting
+
+ private TypeNameParser(ReadOnlySpan name)
+ {
+ _input = name;
+ _errorIndex = _index = 0;
+ }
+
+ //
+ // Parses a type name. The type name may be optionally postpended with a "," followed by a legal assembly name.
+ //
+ private Type? Parse()
+ {
+ TypeName? typeName = ParseNonQualifiedTypeName();
+ if (typeName is null)
+ return null;
+
+ string? assemblyName = null;
+
+ TokenType token = GetNextToken();
+ if (token != TokenType.End)
+ {
+ if (token != TokenType.Comma)
+ {
+ ParseError();
+ return null;
+ }
+
+ if (!CheckTopLevelAssemblyQualifiedName())
+ return null;
+
+ assemblyName = GetNextAssemblyName();
+ Debug.Assert(Peek == TokenType.End);
+ }
+
+ return typeName.ResolveType(ref this, assemblyName);
+ }
+
+ //
+ // Parses a type name without any assembly name qualification.
+ //
+ private TypeName? ParseNonQualifiedTypeName()
+ {
+ // Parse the named type or constructed generic type part first.
+ TypeName? typeName = ParseNamedOrConstructedGenericTypeName();
+ if (typeName is null)
+ return null;
+
+ // Iterate through any "has-element" qualifiers ([], &, *).
+ while (true)
+ {
+ TokenType token = Peek;
+ if (token == TokenType.End)
+ break;
+ if (token == TokenType.Asterisk)
+ {
+ Skip();
+ typeName = new ModifierTypeName(typeName, ModifierTypeName.Pointer);
+ }
+ else if (token == TokenType.Ampersand)
+ {
+ Skip();
+ typeName = new ModifierTypeName(typeName, ModifierTypeName.ByRef);
+ }
+ else if (token == TokenType.OpenSqBracket)
+ {
+ Skip();
+ token = GetNextToken();
+ if (token == TokenType.Asterisk)
+ {
+ typeName = new ModifierTypeName(typeName, 1);
+ token = GetNextToken();
+ }
+ else
+ {
+ int rank = 1;
+ while (token == TokenType.Comma)
+ {
+ token = GetNextToken();
+ rank++;
+ }
+ if (rank == 1)
+ typeName = new ModifierTypeName(typeName, ModifierTypeName.Array);
+ else
+ typeName = new ModifierTypeName(typeName, rank);
+ }
+ if (token != TokenType.CloseSqBracket)
+ {
+ ParseError();
+ return null;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ return typeName;
+ }
+
+ //
+ // Foo or Foo+Inner or Foo[String] or Foo+Inner[String]
+ //
+ private TypeName? ParseNamedOrConstructedGenericTypeName()
+ {
+ TypeName? namedType = ParseNamedTypeName();
+ if (namedType is null)
+ return null;
+
+ // Because "[" is used both for generic arguments and array indexes, we must peek two characters deep.
+ if (!(Peek == TokenType.OpenSqBracket && (PeekSecond == TokenType.Other || PeekSecond == TokenType.OpenSqBracket)))
+ return namedType;
+
+ Skip();
+
+ TypeName[] typeArguments = new TypeName[2];
+ int typeArgumentsCount = 0;
+ while (true)
+ {
+ TypeName? typeArgument = ParseGenericTypeArgument();
+ if (typeArgument is null)
+ return null;
+ if (typeArgumentsCount >= typeArguments.Length)
+ Array.Resize(ref typeArguments, 2 * typeArgumentsCount);
+ typeArguments[typeArgumentsCount++] = typeArgument;
+ TokenType token = GetNextToken();
+ if (token == TokenType.CloseSqBracket)
+ break;
+ if (token != TokenType.Comma)
+ {
+ ParseError();
+ return null;
+ }
+ }
+
+ return new GenericTypeName(namedType, typeArguments, typeArgumentsCount);
+ }
+
+ //
+ // Foo or Foo+Inner
+ //
+ private TypeName? ParseNamedTypeName()
+ {
+ string? fullName = GetNextIdentifier();
+ if (fullName is null)
+ return null;
+
+ if (Peek == TokenType.Plus)
+ {
+ string[] nestedNames = new string[1];
+ int nestedNamesCount = 0;
+
+ do
+ {
+ Skip();
+
+ string? nestedName = GetNextIdentifier();
+ if (nestedName is null)
+ return null;
+
+ if (nestedNamesCount >= nestedNames.Length)
+ Array.Resize(ref nestedNames, 2 * nestedNamesCount);
+ nestedNames[nestedNamesCount++] = nestedName;
+ }
+ while (Peek == TokenType.Plus);
+
+ return new NestedNamespaceTypeName(fullName, nestedNames, nestedNamesCount);
+ }
+ else
+ {
+ return new NamespaceTypeName(fullName);
+ }
+ }
+
+ //
+ // Parse a generic argument. In particular, generic arguments can take the special form [,].
+ //
+ private TypeName? ParseGenericTypeArgument()
+ {
+ TokenType token = GetNextToken();
+ if (token == TokenType.Other)
+ {
+ return ParseNonQualifiedTypeName();
+ }
+ if (token != TokenType.OpenSqBracket)
+ {
+ ParseError();
+ return null;
+ }
+ string? assemblyName = null;
+ TypeName? typeName = ParseNonQualifiedTypeName();
+ if (typeName is null)
+ return null;
+
+ token = GetNextToken();
+ if (token == TokenType.Comma)
+ {
+ assemblyName = GetNextEmbeddedAssemblyName();
+ token = GetNextToken();
+ }
+ if (token != TokenType.CloseSqBracket)
+ {
+ ParseError();
+ return null;
+ }
+
+ return (assemblyName != null) ? new AssemblyQualifiedTypeName(typeName, assemblyName) : typeName;
+ }
+
+ //
+ // String tokenizer for type names passed to the GetType() APIs.
+ //
+
+ private TokenType Peek
+ {
+ get
+ {
+ SkipWhiteSpace();
+ char c = (_index < _input.Length) ? _input[_index] : '\0';
+ return CharToToken(c);
+ }
+ }
+
+ private TokenType PeekSecond
+ {
+ get
+ {
+ SkipWhiteSpace();
+ int index = _index + 1;
+ while (index < _input.Length && char.IsWhiteSpace(_input[index]))
+ index++;
+ char c = (index < _input.Length) ? _input[index] : '\0';
+ return CharToToken(c);
+ }
+ }
+
+ private void Skip()
+ {
+ SkipWhiteSpace();
+ if (_index < _input.Length)
+ _index++;
+ }
+
+ // Return the next token and skip index past it unless already at end of string
+ // or the token is not a reserved token.
+ private TokenType GetNextToken()
+ {
+ _errorIndex = _index;
+
+ TokenType tokenType = Peek;
+ if (tokenType == TokenType.End || tokenType == TokenType.Other)
+ return tokenType;
+ Skip();
+ return tokenType;
+ }
+
+ //
+ // Lex the next segment as part of a type name. (Do not use for assembly names.)
+ //
+ // Note that unescaped "."'s do NOT terminate the identifier, but unescaped "+"'s do.
+ //
+ // Terminated by the first non-escaped reserved character ('[', ']', '+', '&', '*' or ',')
+ //
+ private string? GetNextIdentifier()
+ {
+ SkipWhiteSpace();
+
+ ValueStringBuilder sb = new ValueStringBuilder(stackalloc char[64]);
+
+ int src = _index;
+ while (true)
+ {
+ if (src >= _input.Length)
+ break;
+ char c = _input[src];
+ TokenType token = CharToToken(c);
+ if (token != TokenType.Other)
+ break;
+ src++;
+ if (c == '\\') // Check for escaped character
+ {
+ // Update error location
+ _errorIndex = src - 1;
+
+ c = (src < _input.Length) ? _input[src++] : '\0';
+
+ if (!NeedsEscapingInTypeName(c))
+ {
+ // If we got here, a backslash was used to escape a character that is not legal to escape inside a type name.
+ ParseError();
+ return null;
+ }
+ }
+ sb.Append(c);
+ }
+ _index = src;
+
+ if (sb.Length == 0)
+ {
+ // The identifier has to be non-empty
+ _errorIndex = src;
+ ParseError();
+ return null;
+ }
+
+ return sb.ToString();
+ }
+
+ //
+ // Lex the next segment as the assembly name at the end of an assembly-qualified type name. (Do not use for
+ // assembly names embedded inside generic type arguments.)
+ //
+ private string GetNextAssemblyName()
+ {
+ SkipWhiteSpace();
+
+ string assemblyName = new string(_input.Slice(_index));
+ _index = _input.Length;
+ return assemblyName;
+ }
+
+ //
+ // Lex the next segment as an assembly name embedded inside a generic argument type.
+ //
+ // Terminated by an unescaped ']'.
+ //
+ private string? GetNextEmbeddedAssemblyName()
+ {
+ SkipWhiteSpace();
+
+ ValueStringBuilder sb = new ValueStringBuilder(stackalloc char[64]);
+
+ int src = _index;
+ while (true)
+ {
+ if (src >= _input.Length)
+ {
+ ParseError();
+ return null;
+ }
+ char c = _input[src];
+ if (c == ']')
+ break;
+ src++;
+
+ // Backslash can be used to escape a ']' - any other backslash character is left alone (along with the backslash)
+ // for the AssemblyName parser to handle.
+ if (c == '\\' && (src < _input.Length) && _input[src] == ']')
+ {
+ c = _input[src++];
+ }
+ sb.Append(c);
+ }
+ _index = src;
+
+ if (sb.Length == 0)
+ {
+ // The assembly name has to be non-empty
+ _errorIndex = src;
+ ParseError();
+ return null;
+ }
+
+ return sb.ToString();
+ }
+
+ //
+ // Classify a character as a TokenType. (Fortunately, all tokens in type name strings other than identifiers are single-character tokens.)
+ //
+ private static TokenType CharToToken(char c)
+ {
+ return c switch
+ {
+ '\0' => TokenType.End,
+ '[' => TokenType.OpenSqBracket,
+ ']' => TokenType.CloseSqBracket,
+ ',' => TokenType.Comma,
+ '+' => TokenType.Plus,
+ '*' => TokenType.Asterisk,
+ '&' => TokenType.Ampersand,
+ _ => TokenType.Other,
+ };
+ }
+
+ //
+ // The type name parser has a strange attitude towards whitespace. It throws away whitespace between punctuation tokens and whitespace
+ // preceding identifiers or assembly names (and this cannot be escaped away). But whitespace between the end of an identifier
+ // and the punctuation that ends it is *not* ignored.
+ //
+ // In other words, GetType(" Foo") searches for "Foo" but GetType("Foo ") searches for "Foo ".
+ //
+ // Whitespace between the end of an assembly name and the punction mark that ends it is also not ignored by this parser,
+ // but this is irrelevant since the assembly name is then turned over to AssemblyName for parsing, which *does* ignore trailing whitespace.
+ //
+ private void SkipWhiteSpace()
+ {
+ while (_index < _input.Length && char.IsWhiteSpace(_input[_index]))
+ _index++;
+ }
+
+ private enum TokenType
+ {
+ End = 0, //At end of string
+ OpenSqBracket = 1, //'['
+ CloseSqBracket = 2, //']'
+ Comma = 3, //','
+ Plus = 4, //'+'
+ Asterisk = 5, //'*'
+ Ampersand = 6, //'&'
+ Other = 7, //Type identifier, AssemblyName or embedded AssemblyName.
+ }
+
+ //
+ // The TypeName class is the base class for a family of types that represent the nodes in a parse tree for
+ // assembly-qualified type names.
+ //
+ private abstract class TypeName
+ {
+ ///
+ /// Helper for the Type.GetType() family of APIs. "containingAssemblyIsAny" is the assembly to search for (as determined
+ /// by a qualifying assembly string in the original type string passed to Type.GetType(). If null, it means the type stream
+ /// didn't specify an assembly name. How to respond to that is up to the type resolver delegate in getTypeOptions - this class
+ /// is just a middleman.
+ ///
+ public abstract Type? ResolveType(ref TypeNameParser parser, string? containingAssemblyIfAny);
+ }
+
+ //
+ // Represents a parse of a type name qualified by an assembly name.
+ //
+ private sealed class AssemblyQualifiedTypeName : TypeName
+ {
+ private readonly string _assemblyName;
+ private readonly TypeName _nonQualifiedTypeName;
+
+ public AssemblyQualifiedTypeName(TypeName nonQualifiedTypeName, string assemblyName)
+ {
+ _nonQualifiedTypeName = nonQualifiedTypeName;
+ _assemblyName = assemblyName;
+ }
+
+ public override Type? ResolveType(ref TypeNameParser parser, string? containingAssemblyIfAny)
+ {
+ return _nonQualifiedTypeName.ResolveType(ref parser, _assemblyName);
+ }
+ }
+
+ //
+ // Non-nested named type. The full name is the namespace-qualified name. For example, the FullName for
+ // System.Collections.Generic.IList<> is "System.Collections.Generic.IList`1".
+ //
+ private sealed partial class NamespaceTypeName : TypeName
+ {
+ private readonly string _fullName;
+ public NamespaceTypeName(string fullName)
+ {
+ _fullName = fullName;
+ }
+
+ public override Type? ResolveType(ref TypeNameParser parser, string? containingAssemblyIfAny)
+ {
+ return parser.GetType(_fullName, default, containingAssemblyIfAny);
+ }
+ }
+
+ //
+ // Nested type name.
+ //
+ private sealed partial class NestedNamespaceTypeName : TypeName
+ {
+ private readonly string _fullName;
+ private readonly string[] _nestedNames;
+ private readonly int _nestedNamesCount;
+
+ public NestedNamespaceTypeName(string fullName, string[] nestedNames, int nestedNamesCount)
+ {
+ _fullName = fullName;
+ _nestedNames = nestedNames;
+ _nestedNamesCount = nestedNamesCount;
+ }
+
+ public override Type? ResolveType(ref TypeNameParser parser, string? containingAssemblyIfAny)
+ {
+ return parser.GetType(_fullName, _nestedNames.AsSpan(0, _nestedNamesCount), containingAssemblyIfAny);
+ }
+ }
+
+ //
+ // Array, byref or pointer type name.
+ //
+ private sealed class ModifierTypeName : TypeName
+ {
+ private readonly TypeName _elementTypeName;
+
+ // Positive value is multi-dimensional array rank.
+ // Negative value is modifier encoded using constants below.
+ private readonly int _rankOrModifier;
+
+ public const int Array = -1;
+ public const int Pointer = -2;
+ public const int ByRef = -3;
+
+ public ModifierTypeName(TypeName elementTypeName, int rankOrModifier)
+ {
+ _elementTypeName = elementTypeName;
+ _rankOrModifier = rankOrModifier;
+ }
+
+ [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi",
+ Justification = "Used to implement resolving types from strings.")]
+ public override Type? ResolveType(ref TypeNameParser parser, string? containingAssemblyIfAny)
+ {
+ Type? elementType = _elementTypeName.ResolveType(ref parser, containingAssemblyIfAny);
+ if (elementType is null)
+ return null;
+
+ return _rankOrModifier switch
+ {
+ Array => elementType.MakeArrayType(),
+ Pointer => elementType.MakePointerType(),
+ ByRef => elementType.MakeByRefType(),
+ _ => elementType.MakeArrayType(_rankOrModifier)
+ };
+ }
+ }
+
+ //
+ // Constructed generic type name.
+ //
+ private sealed class GenericTypeName : TypeName
+ {
+ private readonly TypeName _typeDefinition;
+ private readonly TypeName[] _typeArguments;
+ private readonly int _typeArgumentsCount;
+
+ public GenericTypeName(TypeName genericTypeDefinition, TypeName[] typeArguments, int typeArgumentsCount)
+ {
+ _typeDefinition = genericTypeDefinition;
+ _typeArguments = typeArguments;
+ _typeArgumentsCount = typeArgumentsCount;
+ }
+
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2055:UnrecognizedReflectionPattern",
+ Justification = "Used to implement resolving types from strings.")]
+ [UnconditionalSuppressMessage("AotAnalysis", "IL3050:AotUnfriendlyApi",
+ Justification = "Used to implement resolving types from strings.")]
+ public override Type? ResolveType(ref TypeNameParser parser, string? containingAssemblyIfAny)
+ {
+ Type? typeDefinition = _typeDefinition.ResolveType(ref parser, containingAssemblyIfAny);
+ if (typeDefinition is null)
+ return null;
+
+ Type[] arguments = new Type[_typeArgumentsCount];
+ for (int i = 0; i < arguments.Length; i++)
+ {
+ Type? typeArgument = _typeArguments[i].ResolveType(ref parser, null);
+ if (typeArgument is null)
+ return null;
+ arguments[i] = typeArgument;
+ }
+
+ return typeDefinition.MakeGenericType(arguments);
+ }
+ }
+
+ //
+ // Type name escaping helpers
+ //
+
+ private static ReadOnlySpan CharsToEscape => "\\[]+*&,";
+
+ private static bool NeedsEscapingInTypeName(char c)
+ => CharsToEscape.Contains(c);
+
+ private static string EscapeTypeName(string name)
+ {
+ if (name.AsSpan().IndexOfAny(CharsToEscape) < 0)
+ return name;
+
+ var sb = new ValueStringBuilder(stackalloc char[64]);
+ foreach (char c in name)
+ {
+ if (NeedsEscapingInTypeName(c))
+ sb.Append('\\');
+ sb.Append(c);
+ }
+
+ return sb.ToString();
+ }
+
+ private static string EscapeTypeName(string typeName, ReadOnlySpan nestedTypeNames)
+ {
+ string fullName = EscapeTypeName(typeName);
+ if (nestedTypeNames.Length > 0)
+ {
+ var sb = new StringBuilder(fullName);
+ for (int i = 0; i < nestedTypeNames.Length; i++)
+ {
+ sb.Append('+');
+ sb.Append(EscapeTypeName(nestedTypeNames[i]));
+ }
+ fullName = sb.ToString();
+ }
+ return fullName;
+ }
+
+ private void ParseError()
+ {
+ if (_throwOnError)
+ throw new ArgumentException(SR.Arg_ArgumentException, $"typeName@{_errorIndex}");
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index 77ba3fe451347..cd44b2e9e5c63 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -1360,6 +1360,9 @@
Common\System\IO\PathInternal.CaseSensitivity.cs
+
+ Common\System\Reflection\TypeNameParser.cs
+
Common\System\Runtime\Versioning\NonVersionableAttribute.cs
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs
index 7bdcba19384e7..53de721a0c458 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs
@@ -711,20 +711,20 @@ private static void OnAssemblyLoad(RuntimeAssembly assembly)
}
// This method is called by the VM
- private static RuntimeAssembly? OnTypeResolve(RuntimeAssembly assembly, string typeName)
+ internal static RuntimeAssembly? OnTypeResolve(RuntimeAssembly? assembly, string typeName)
{
return InvokeResolveEvent(TypeResolve, assembly, typeName);
}
// This method is called by the VM.
- private static RuntimeAssembly? OnAssemblyResolve(RuntimeAssembly assembly, string assemblyFullName)
+ private static RuntimeAssembly? OnAssemblyResolve(RuntimeAssembly? assembly, string assemblyFullName)
{
return InvokeResolveEvent(AssemblyResolve, assembly, assemblyFullName);
}
[UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file",
Justification = "The code handles the Assembly.Location equals null")]
- private static RuntimeAssembly? InvokeResolveEvent(ResolveEventHandler? eventHandler, RuntimeAssembly assembly, string name)
+ private static RuntimeAssembly? InvokeResolveEvent(ResolveEventHandler? eventHandler, RuntimeAssembly? assembly, string name)
{
if (eventHandler == null)
return null;
diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
index 22567dc0f2e0f..fe289bb47697e 100644
--- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -186,7 +186,6 @@
-
@@ -245,6 +244,7 @@
+
diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameParser.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameParser.Mono.cs
new file mode 100644
index 0000000000000..3622b4e27626a
--- /dev/null
+++ b/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameParser.Mono.cs
@@ -0,0 +1,165 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.Loader;
+using System.Text;
+using System.Threading;
+
+namespace System.Reflection
+{
+ internal unsafe ref partial struct TypeNameParser
+ {
+ private Func? _assemblyResolver;
+ private Func? _typeResolver;
+ private bool _throwOnError;
+ private bool _ignoreCase;
+ private void* _stackMark;
+
+ [RequiresUnreferencedCode("The type might be removed")]
+ internal static Type? GetType(
+ string typeName,
+ Func? assemblyResolver,
+ Func? typeResolver,
+ bool throwOnError,
+ bool ignoreCase,
+ ref StackCrawlMark stackMark)
+ {
+ ArgumentNullException.ThrowIfNull(typeName);
+
+ // Compat: Empty name throws TypeLoadException instead of
+ // the natural ArgumentException
+ if (typeName.Length == 0)
+ {
+ if (throwOnError)
+ throw new TypeLoadException(SR.Arg_TypeLoadNullStr);
+ return null;
+ }
+
+ return new TypeNameParser(typeName)
+ {
+ _assemblyResolver = assemblyResolver,
+ _typeResolver = typeResolver,
+ _throwOnError = throwOnError,
+ _ignoreCase = ignoreCase,
+ _stackMark = Unsafe.AsPointer(ref stackMark)
+ }.Parse();
+ }
+
+ private static bool CheckTopLevelAssemblyQualifiedName()
+ {
+ return true;
+ }
+
+ private Assembly? ResolveAssembly(string assemblyName)
+ {
+ var name = new AssemblyName(assemblyName);
+
+ Assembly? assembly;
+ if (_assemblyResolver is not null)
+ {
+ assembly = _assemblyResolver(name);
+ if (assembly is null && _throwOnError)
+ {
+ throw new FileNotFoundException(SR.Format(SR.FileNotFound_ResolveAssembly, assemblyName));
+ }
+ }
+ else
+ {
+ ref StackCrawlMark stackMark = ref Unsafe.AsRef(_stackMark);
+
+ if (_throwOnError)
+ {
+ assembly = Assembly.Load(name, ref stackMark, null);
+ }
+ else
+ {
+ // When throwOnError is false we should only catch FileNotFoundException.
+ // Other exceptions like BadImangeFormatException should still fly.
+ try
+ {
+ assembly = Assembly.Load(name, ref stackMark, null);
+ }
+ catch (FileNotFoundException)
+ {
+ return null;
+ }
+ }
+ }
+ return assembly;
+ }
+
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
+ Justification = "TypeNameParser.GetType is marked as RequiresUnreferencedCode.")]
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
+ Justification = "TypeNameParser.GetType is marked as RequiresUnreferencedCode.")]
+ private Type? GetType(string typeName, ReadOnlySpan nestedTypeNames, string? assemblyNameIfAny)
+ {
+ Assembly? assembly = (assemblyNameIfAny is not null) ? ResolveAssembly(assemblyNameIfAny) : null;
+
+ // Both the external type resolver and the default type resolvers expect escaped type names
+ string escapedTypeName = EscapeTypeName(typeName);
+
+ Type? type;
+
+ // Resolve the top level type.
+ if (_typeResolver is not null)
+ {
+ type = _typeResolver(assembly, escapedTypeName, _ignoreCase);
+
+ if (type is null)
+ {
+ if (_throwOnError)
+ {
+ throw new TypeLoadException(assembly is null ?
+ SR.Format(SR.TypeLoad_ResolveType, escapedTypeName) :
+ SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, escapedTypeName, assembly.FullName));
+ }
+ return null;
+ }
+ }
+ else
+ {
+ if (assembly is null)
+ {
+ ref StackCrawlMark stackMark = ref Unsafe.AsRef(_stackMark);
+
+ type = RuntimeType.GetType(escapedTypeName, _throwOnError, _ignoreCase, ref stackMark);
+ }
+ else
+ {
+ type = assembly.GetType(escapedTypeName, _throwOnError, _ignoreCase);
+ }
+
+ if (type is null)
+ {
+ return null;
+ }
+ }
+
+ for (int i = 0; i < nestedTypeNames.Length; i++)
+ {
+ BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public;
+ if (_ignoreCase)
+ bindingFlags |= BindingFlags.IgnoreCase;
+
+ type = type.GetNestedType(nestedTypeNames[i], bindingFlags);
+
+ if (type is null)
+ {
+ if (_throwOnError)
+ {
+ throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveNestedType,
+ nestedTypeNames[i], (i > 0) ? nestedTypeNames[i - 1] : typeName));
+ }
+ return null;
+ }
+ }
+
+ return type;
+ }
+ }
+}
diff --git a/src/mono/System.Private.CoreLib/src/System/TypeNameParser.cs b/src/mono/System.Private.CoreLib/src/System/TypeNameParser.cs
deleted file mode 100644
index db6db621a3231..0000000000000
--- a/src/mono/System.Private.CoreLib/src/System/TypeNameParser.cs
+++ /dev/null
@@ -1,449 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Text;
-using System.IO;
-using System.Diagnostics.CodeAnalysis;
-using System.Reflection;
-using System.Collections.Generic;
-using System.Threading;
-
-namespace System
-{
- internal static class TypeNameParser
- {
- private static readonly char[] SPECIAL_CHARS = { ',', '[', ']', '&', '*', '+', '\\' };
-
- [RequiresUnreferencedCode("Types might be removed")]
- internal static Type? GetType(
- string typeName,
- Func? assemblyResolver,
- Func? typeResolver,
- bool throwOnError,
- bool ignoreCase,
- ref StackCrawlMark stackMark)
- {
- ArgumentNullException.ThrowIfNull(typeName);
-
- ParsedName? pname = ParseName(typeName, false, 0, out int end_pos);
- if (pname == null)
- {
- if (throwOnError)
- throw new ArgumentException();
- return null;
- }
-
- return ConstructType(pname, assemblyResolver, typeResolver, throwOnError, ignoreCase, ref stackMark);
- }
-
- [RequiresUnreferencedCode("Types might be removed")]
- private static Type? ConstructType(
- ParsedName pname,
- Func? assemblyResolver,
- Func? typeResolver,
- bool throwOnError,
- bool ignoreCase,
- ref StackCrawlMark stackMark)
- {
- // Resolve assembly
- Assembly? assembly = null;
- if (pname.AssemblyName != null)
- {
- assembly = ResolveAssembly(pname.AssemblyName, assemblyResolver, throwOnError, ref stackMark);
- if (assembly == null)
- // If throwOnError is true, an exception was already thrown
- return null;
- }
-
- // Resolve base type
- Type? type = ResolveType(assembly!, pname.Names!, typeResolver, throwOnError, ignoreCase, ref stackMark);
- if (type == null)
- return null;
-
- // Resolve type arguments
- if (pname.TypeArguments != null)
- {
- var args = new Type?[pname.TypeArguments.Count];
- for (int i = 0; i < pname.TypeArguments.Count; ++i)
- {
- args[i] = ConstructType(pname.TypeArguments[i], assemblyResolver, typeResolver, throwOnError, ignoreCase, ref stackMark);
- if (args[i] == null)
- return null;
- }
- type = type.MakeGenericType(args!);
- }
-
- // Resolve modifiers
- if (pname.Modifiers != null)
- {
- bool bounded = false;
- foreach (int mod in pname.Modifiers)
- {
- switch (mod)
- {
- case 0:
- type = type.MakeByRefType();
- break;
- case -1:
- type = type.MakePointerType();
- break;
- case -2:
- bounded = true;
- break;
- case 1:
- if (bounded)
- type = type.MakeArrayType(1);
- else
- type = type.MakeArrayType();
- break;
- default:
- type = type.MakeArrayType(mod);
- break;
- }
- }
- }
-
- return type;
- }
-
- private static Assembly? ResolveAssembly(string name, Func? assemblyResolver, bool throwOnError,
- ref StackCrawlMark stackMark)
- {
- var aname = new AssemblyName(name);
-
- if (assemblyResolver == null)
- {
- if (throwOnError)
- {
- return Assembly.Load(aname, ref stackMark, null);
- }
- else
- {
- try
- {
- return Assembly.Load(aname, ref stackMark, null);
- }
- catch (FileNotFoundException)
- {
- return null;
- }
- }
- }
- else
- {
- Assembly? assembly = assemblyResolver(aname);
- if (assembly == null && throwOnError)
- throw new FileNotFoundException(SR.FileNotFound_ResolveAssembly, name);
- return assembly;
- }
- }
-
- [RequiresUnreferencedCode("Types might be removed")]
- private static Type? ResolveType(Assembly assembly, List names, Func? typeResolver, bool throwOnError, bool ignoreCase, ref StackCrawlMark stackMark)
- {
- Type? type;
-
- string name = EscapeTypeName(names[0]);
- // Resolve the top level type.
- if (typeResolver != null)
- {
- type = typeResolver(assembly, name, ignoreCase);
- if (type == null && throwOnError)
- {
- if (assembly == null)
- throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveType, name));
- else
- throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, name, assembly.FullName));
- }
- }
- else
- {
- if (assembly == null)
- type = RuntimeType.GetType(name, throwOnError, ignoreCase, ref stackMark);
- else
- type = assembly.GetType(name, throwOnError, ignoreCase);
- }
-
- if (type == null)
- return null;
-
- // Resolve nested types.
- BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public;
- if (ignoreCase)
- bindingFlags |= BindingFlags.IgnoreCase;
-
- for (int i = 1; i < names.Count; ++i)
- {
- type = type.GetNestedType(names[i], bindingFlags);
- if (type == null)
- {
- if (throwOnError)
- throw new TypeLoadException(SR.Format(SR.TypeLoad_ResolveNestedType, names[i], names[i - 1]));
- else
- break;
- }
- }
- return type;
- }
-
- private static string EscapeTypeName(string name)
- {
- if (name.IndexOfAny(SPECIAL_CHARS) < 0)
- return name;
-
- var sb = new StringBuilder(name.Length);
- foreach (char c in name)
- {
- if (Array.IndexOf(SPECIAL_CHARS, c) >= 0)
- sb.Append('\\');
- sb.Append(c);
- }
-
- return sb.ToString();
- }
-
- private static string UnescapeTypeName(string name)
- {
- if (name.IndexOfAny(SPECIAL_CHARS) < 0)
- return name;
-
- var sb = new StringBuilder(name.Length - 1);
- for (int i = 0; i < name.Length; ++i)
- {
- if (name[i] == '\\' && i + 1 < name.Length)
- i++;
- sb.Append(name[i]);
- }
-
- return sb.ToString();
- }
-
- private sealed class ParsedName
- {
- public List? Names;
- public List? TypeArguments;
- public List? Modifiers;
- public string? AssemblyName;
-
- /* For debugging
- public override string ToString () {
- var sb = new StringBuilder ();
- sb.Append (Names [0]);
- if (TypeArguments != null) {
- sb.Append ("[");
- for (int i = 0; i < TypeArguments.Count; ++i) {
- if (TypeArguments [i].AssemblyName != null)
- sb.Append ('[');
- sb.Append (TypeArguments [i].ToString ());
- if (TypeArguments [i].AssemblyName != null)
- sb.Append (']');
- if (i < TypeArguments.Count - 1)
- sb.Append (", ");
- }
- sb.Append ("]");
- }
- if (AssemblyName != null)
- sb.Append ($", {AssemblyName}");
- return sb.ToString ();
- }
- */
- }
-
- // Ported from the C version in mono_reflection_parse_type_checked ()
- // Entries to the Names list are unescaped to internal form while AssemblyName is not, in an effort to maintain
- // consistency with our native parser. Since this function is just called recursively, that should also be true
- // for ParsedNames in TypeArguments.
- private static ParsedName? ParseName(string name, bool recursed, int pos, out int end_pos)
- {
- end_pos = 0;
-
- while (pos < name.Length && name[pos] == ' ')
- pos++;
-
- var res = new ParsedName() { Names = new List() };
-
- int name_start = pos;
- bool in_modifiers = false;
- while (pos < name.Length)
- {
- switch (name[pos])
- {
- case '+':
- res.Names.Add(UnescapeTypeName(name.Substring(name_start, pos - name_start)));
- name_start = pos + 1;
- break;
- case '\\':
- pos++;
- break;
- case '&':
- case '*':
- case '[':
- case ',':
- case ']':
- in_modifiers = true;
- break;
- default:
- break;
- }
- if (in_modifiers)
- break;
- pos++;
- }
-
- res.Names.Add(UnescapeTypeName(name.Substring(name_start, pos - name_start)));
-
- bool isbyref = false;
- bool isptr = false;
- int rank = -1;
-
- bool end = false;
- while (pos < name.Length && !end)
- {
- switch (name[pos])
- {
- case '&':
- if (isbyref)
- return null;
- pos++;
- isbyref = true;
- isptr = false;
- res.Modifiers ??= new List();
- res.Modifiers.Add(0);
- break;
- case '*':
- if (isbyref)
- return null;
- pos++;
- res.Modifiers ??= new List();
- res.Modifiers.Add(-1);
- isptr = true;
- break;
- case '[':
- // An array or generic arguments
- if (isbyref)
- return null;
- pos++;
- if (pos == name.Length)
- return null;
-
- if (name[pos] == ',' || name[pos] == '*' || name[pos] == ']')
- {
- // Array
- bool bounded = false;
- isptr = false;
- rank = 1;
- while (pos < name.Length)
- {
- if (name[pos] == ']')
- break;
- if (name[pos] == ',')
- rank++;
- else if (name[pos] == '*') /* '*' means unknown lower bound */
- bounded = true;
- else
- return null;
- pos++;
- }
- if (pos == name.Length)
- return null;
- if (name[pos] != ']')
- return null;
- pos++;
- /* bounded only allowed when rank == 1 */
- if (bounded && rank > 1)
- return null;
- /* n.b. bounded needs both modifiers: -2 == bounded, 1 == rank 1 array */
- res.Modifiers ??= new List();
- if (bounded)
- res.Modifiers.Add(-2);
- res.Modifiers.Add(rank);
- }
- else
- {
- // Generic args
- if (rank > 0 || isptr)
- return null;
- isptr = false;
- res.TypeArguments = new List();
- while (pos < name.Length)
- {
- while (pos < name.Length && name[pos] == ' ')
- pos++;
- bool fqname = false;
- if (pos < name.Length && name[pos] == '[')
- {
- pos++;
- fqname = true;
- }
-
- ParsedName? arg = ParseName(name, true, pos, out pos);
- if (arg == null)
- return null;
- res.TypeArguments.Add(arg);
-
- /*MS is lenient on [] delimited parameters that aren't fqn - and F# uses them.*/
- if (fqname && pos < name.Length && name[pos] != ']')
- {
- if (name[pos] != ',')
- return null;
- pos++;
- int aname_start = pos;
- while (pos < name.Length && name[pos] != ']')
- pos++;
- if (pos == name.Length)
- return null;
- while (char.IsWhiteSpace(name[aname_start]))
- aname_start++;
- if (aname_start == pos)
- return null;
- arg.AssemblyName = name.Substring(aname_start, pos - aname_start);
- pos++;
- }
- else if (fqname && pos < name.Length && name[pos] == ']')
- {
- pos++;
- }
- if (pos < name.Length && name[pos] == ']')
- {
- pos++;
- break;
- }
- else if (pos == name.Length)
- return null;
- pos++;
- }
- }
- break;
- case ']':
- if (recursed)
- {
- end = true;
- break;
- }
- return null;
- case ',':
- if (recursed)
- {
- end = true;
- break;
- }
- pos++;
- while (pos < name.Length && char.IsWhiteSpace(name[pos]))
- pos++;
- if (pos == name.Length)
- return null;
- res.AssemblyName = name.Substring(pos);
- end = true;
- break;
- default:
- return null;
- }
- if (end)
- break;
- }
-
- end_pos = pos;
- return res;
- }
- }
-}