From f4f76e56a0f2c6181ddfd6de5d66f3f812cf8803 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sun, 18 Sep 2022 09:06:09 -0700 Subject: [PATCH 1/9] Unify managed runtime type name parsers --- .../System.Private.CoreLib.csproj | 2 +- .../src/System/Reflection/RuntimeAssembly.cs | 76 ++- .../src/System/Reflection/RuntimeModule.cs | 10 +- .../Reflection/TypeNameParser.CoreCLR.cs | 239 +++++++ .../src/System/RuntimeHandles.cs | 6 - .../src/System/RuntimeType.CoreCLR.cs | 11 +- .../src/System/Type.CoreCLR.cs | 19 +- .../src/System/TypeNameParser.cs | 341 ---------- .../Core/Execution/ExecutionDomain.cs | 91 +-- .../ReflectionExecutionDomainCallbacks.cs | 2 +- .../CompilerHelpers/ReflectionHelpers.cs | 6 +- .../src/System.Private.CoreLib.csproj | 5 +- ...imeAssembly.GetTypeCore.CaseInsensitive.cs | 12 +- ...ntimeAssembly.GetTypeCore.CaseSensitive.cs | 8 - .../NativeFormatRuntimeAssembly.cs | 14 +- .../Runtime/Assemblies/RuntimeAssemblyInfo.cs | 57 +- .../Reflection/Runtime/General/Helpers.cs | 19 - .../General/TypeResolver.NativeFormat.cs | 9 +- .../Runtime/General/TypeResolver.cs | 12 - .../Runtime/TypeParsing/GetTypeOptions.cs | 67 -- .../Runtime/TypeParsing/TypeLexer.cs | 243 ------- .../Runtime/TypeParsing/TypeName.cs | 322 --------- .../Runtime/TypeParsing/TypeParser.cs | 222 ------ .../Reflection/TypeNameParser.NativeAot.cs | 222 ++++++ .../src/System/Type.NativeAot.cs | 4 +- ...nExecutionDomainCallbacksImplementation.cs | 2 +- .../Execution/ReflectionExecution.cs | 21 - ...nExecutionDomainCallbacksImplementation.cs | 4 +- src/coreclr/vm/appdomain.cpp | 3 + src/coreclr/vm/assemblynative.cpp | 114 +++- src/coreclr/vm/assemblynative.hpp | 5 +- src/coreclr/vm/commodule.cpp | 46 -- src/coreclr/vm/commodule.h | 5 - src/coreclr/vm/corelib.h | 4 - src/coreclr/vm/qcallentrypoints.cpp | 10 +- src/coreclr/vm/typeparse.cpp | 287 +------- src/coreclr/vm/typeparse.h | 14 +- .../src/System/Reflection/TypeNameParser.cs | 632 ++++++++++++++++++ .../System.Private.CoreLib.Shared.projitems | 3 + .../Runtime/Loader/AssemblyLoadContext.cs | 6 +- .../System.Private.CoreLib.csproj | 2 +- .../System/Reflection/TypeNameParser.Mono.cs | 170 +++++ .../src/System/TypeNameParser.cs | 449 ------------- 43 files changed, 1487 insertions(+), 2309 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/TypeNameParser.cs delete mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/GetTypeOptions.cs delete mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeLexer.cs delete mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeName.cs delete mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs create mode 100644 src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/TypeNameParser.NativeAot.cs create mode 100644 src/libraries/Common/src/System/Reflection/TypeNameParser.cs create mode 100644 src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameParser.Mono.cs delete mode 100644 src/mono/System.Private.CoreLib/src/System/TypeNameParser.cs 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..2259d29d5e145 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs @@ -0,0 +1,239 @@ +// 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 ReadOnlySpan _input; + private int _index; + private int _errorIndex; // Position for error reporting + + 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); + else + 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 != null) + { + if (_throwOnError) + throw new ArgumentException(SR.Argument_AssemblyGetTypeCannotSpecifyAssembly); + return false; + } + return true; + } + + private Assembly? ResolveAssembly(string assemblyName) + { + Assembly? assembly; + if (_assemblyResolver != null) + { + assembly = _assemblyResolver(new AssemblyName(assemblyName)); + if (assembly == 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 != null) + { + assembly = ResolveAssembly(assemblyNameIfAny); + if (assembly == null) + return null; + } + else + { + assembly = _topLevelAssembly; + } + + Type? type; + + // Resolve the top level type. + if (_typeResolver != null) + { + string escapedTypeName = EscapeTypeName(typeName); + + type = _typeResolver(assembly, escapedTypeName, _ignoreCase); + + if (type == null) + { + if (_throwOnError) + { + throw new TypeLoadException(assembly == null ? + SR.Format(SR.TypeLoad_ResolveType, escapedTypeName) : + SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, escapedTypeName, assembly.FullName)); + } + return null; + } + } + else + { + if (assembly == 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 == 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 == 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 != null) + { + Type? type = ((RuntimeAssembly)requestingAssembly).GetTypeCore(typeName, nestedTypeNames, throwOnError: false, ignoreCase: _ignoreCase); + if (type != 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 != null) + return type; + } + + RuntimeAssembly? resolvedAssembly = AssemblyLoadContext.OnTypeResolve(requestingAssembly, EscapeTypeName(typeName, nestedTypeNames)); + if (resolvedAssembly != null) + { + Type? type = resolvedAssembly.GetTypeCore(typeName, nestedTypeNames, throwOnError: false, ignoreCase: _ignoreCase); + if (type != 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/Execution/ExecutionDomain.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionDomain.cs index 42db1ce893a91..312d44bbe6f52 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; @@ -39,86 +38,16 @@ internal ExecutionDomain(ReflectionDomainSetup executionDomainSetup, ExecutionEn // // 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) + public Type GetType(string typeName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase, bool extensibleParser, IList defaultAssemblyNames) { - 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; - }; - } + return TypeNameParser.GetType( + typeName, + assemblyResolver: assemblyResolver, + typeResolver: typeResolver, + throwOnError: throwOnError, + ignoreCase: ignoreCase, + extensibleParser: extensibleParser, + defaultAssemblyNames: defaultAssemblyNames); } // 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..f4e9115121682 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 @@ -26,7 +26,7 @@ namespace Internal.Runtime.Augments 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 Type GetType(string typeName, Func? assemblyResolver, Func? typeResolver, bool throwOnError, bool ignoreCase, bool extensibleParser, string defaultAssembly); public abstract IntPtr TryGetStaticClassConstructionContext(RuntimeTypeHandle runtimeTypeHandle); 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..14f2f253f833e 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 @@ -17,14 +17,16 @@ 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 RuntimeAugments.Callbacks.GetType(typeName, null, null, + throwOnError: throwOnError, ignoreCase: ignoreCase, extensibleParser: false, 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 RuntimeAugments.Callbacks.GetType(typeName, assemblyResolver, typeResolver, + throwOnError: throwOnError, ignoreCase: ignoreCase, extensibleParser: true, 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..d64ced08c3ea4 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/TypeNameParser.NativeAot.cs @@ -0,0 +1,222 @@ +// 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 ReadOnlySpan _input; + private int _index; + private int _errorIndex; // Position for error reporting + + private Func? _assemblyResolver; + private Func? _typeResolver; + private bool _throwOnError; + private bool _ignoreCase; + private bool _extensibleParser; + private Assembly _topLevelAssembly; + private IList _defaultAssemblyNames; + + // [RequiresUnreferencedCode("The type might be removed")] + internal static Type? GetType( + string typeName, + Func? assemblyResolver = null, + Func? typeResolver = null, + bool throwOnError = false, + bool ignoreCase = false, + bool extensibleParser = false, + Assembly topLevelAssembly = null, + IList defaultAssemblyNames = 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); + else + return null; + } + + return new TypeNameParser(typeName) + { + _assemblyResolver = assemblyResolver, + _typeResolver = typeResolver, + _throwOnError = throwOnError, + _ignoreCase = ignoreCase, + _extensibleParser = extensibleParser, + _topLevelAssembly = topLevelAssembly, + _defaultAssemblyNames = defaultAssemblyNames + }.Parse(); + } + + private bool CheckTopLevelAssemblyQualifiedName() + { + if (_topLevelAssembly != null) + { + if (_throwOnError) + throw new ArgumentException(SR.Argument_AssemblyGetTypeCannotSpecifyAssembly); + return false; + } + return true; + } + + private Assembly? ResolveAssembly(string assemblyName) + { + Assembly? assembly; + if (_assemblyResolver != null) + { + assembly = _assemblyResolver(new AssemblyName(assemblyName)); + } + else + { + assembly = RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(RuntimeAssemblyName.Parse(assemblyName)); + } + + if (assembly == null && _throwOnError) + { + throw new FileNotFoundException(SR.Format(SR.FileNotFound_ResolveAssembly, assemblyName)); + } + + 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 != null) + { + assembly = ResolveAssembly(assemblyNameIfAny); + if (assembly == null) + return null; + } + else + { + assembly = _topLevelAssembly; + } + + Type? type = null; + + // Resolve the top level type. + if (_typeResolver != null) + { + string escapedTypeName = EscapeTypeName(typeName); + + type = _typeResolver(assembly, escapedTypeName, _ignoreCase); + + if (type == null) + { + if (_throwOnError) + { + throw new TypeLoadException(assembly == null ? + SR.Format(SR.TypeLoad_ResolveType, escapedTypeName) : + SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, escapedTypeName, assembly.FullName)); + } + return null; + } + } + else + { + if (assembly != 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 == null) + return null; + } + else + { + Debug.Assert(_defaultAssemblyNames != null); + + foreach (string defaultAssemblyName in _defaultAssemblyNames) + { + RuntimeAssemblyName runtimeAssemblyName = RuntimeAssemblyName.Parse(defaultAssemblyName); + RuntimeAssemblyInfo defaultAssembly = RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(runtimeAssemblyName); + if (defaultAssembly == null) + continue; + type = defaultAssembly.GetTypeCore(typeName, throwOnError: false, ignoreCase: _ignoreCase); + if (type != null) + break; + } + + if (type == null) + { + if (_throwOnError) + { + if (_defaultAssemblyNames.Count > 0) + throw Helpers.CreateTypeLoadException(typeName, _defaultAssemblyNames[0]); + else + throw new TypeLoadException(SR.Format(SR.TypeLoad_TypeNotFound, typeName)); + } + 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 == 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 == 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..e8df778ded4e3 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,7 @@ 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) => RuntimeAugments.Callbacks.GetType(typeName, null, null, throwOnError: throwOnError, ignoreCase: ignoreCase, extensibleParser: false, defaultAssembly: null); [Intrinsic] [RequiresUnreferencedCode("The type might be removed")] @@ -81,6 +81,6 @@ 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) => RuntimeAugments.Callbacks.GetType(typeName, assemblyResolver, typeResolver, throwOnError: throwOnError, ignoreCase: ignoreCase, extensibleParser: true, defaultAssembly: null); } } 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..3985797c3938c 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,7 @@ 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 Type GetType(string typeName, Func assemblyResolver, Func? typeResolver, bool throwOnError, bool ignoreCase, bool extensibleParser, 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..a7f0ee2a1ac59 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,13 +31,13 @@ public ReflectionExecutionDomainCallbacksImplementation(ExecutionDomain executio _executionEnvironment = executionEnvironment; } - public sealed override Type GetType(string typeName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase, string defaultAssemblyName) + public sealed override Type GetType(string typeName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase, bool extensibleParser, 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); + return _executionDomain.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, extensibleParser, defaultAssemblies); } public sealed override bool IsReflectionBlocked(RuntimeTypeHandle 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..4eaeac5e12dbb 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -335,51 +335,121 @@ 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 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 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) + { // It is 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 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 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) + { // It is 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..096848f73650d 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 cNestedTypeNamesLength, QCall::ObjectHandleOnStack retType); + + +extern "C" void QCALLTYPE AssemblyNative_GetTypeCoreIgnoreCase(QCall::AssemblyHandle pAssembly, LPCWSTR wszTypeName, LPCWSTR* rgwszNestedTypeNames, INT32 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..397dea076ee6d 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,7 +1075,7 @@ 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; @@ -1320,54 +1087,6 @@ TypeName::GetTypeHaveAssemblyHelper( // 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..6f74d05d456a9 --- /dev/null +++ b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs @@ -0,0 +1,632 @@ +// 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.Text; + +namespace System.Reflection +{ + // + // Parser for type names passed to GetType() apis. + // + internal ref partial struct TypeNameParser + { + 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 == '\\') + { + // 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) + { + switch (c) + { + case '\0': + 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 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) + { + Debug.Assert(nonQualifiedTypeName != null); + Debug.Assert(assemblyName != null); + _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); + } + } + + // + // Abstract base for array, byref and pointer type names. + // + private sealed class ModifierTypeName : TypeName + { + private readonly TypeName _elementTypeName; + private readonly int _rankOrModifier; + + public const int Array = 0; + public const int Pointer = -1; + public const int ByRef = -2; + + 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) + }; + } + } + + // + // A constructed generic type. + // + 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..d41b26d1606ce --- /dev/null +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameParser.Mono.cs @@ -0,0 +1,170 @@ +// 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 ReadOnlySpan _input; + private int _index; + private int _errorIndex; // Position for error reporting + + 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); + else + 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 != null) + { + assembly = _assemblyResolver(name); + if (assembly == 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 != 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 != null) + { + type = _typeResolver(assembly, escapedTypeName, _ignoreCase); + + if (type == null) + { + if (_throwOnError) + { + throw new TypeLoadException(assembly == null ? + SR.Format(SR.TypeLoad_ResolveType, escapedTypeName) : + SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, escapedTypeName, assembly.FullName)); + } + return null; + } + } + else + { + if (assembly == 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 == 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 == 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; - } - } -} From eaea235e2d60449cc70e3adedf3d0bd0e4623ac0 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sun, 19 Mar 2023 08:43:57 -0700 Subject: [PATCH 2/9] Update RUC annotations --- .../src/System/Reflection/TypeNameParser.NativeAot.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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 index d64ced08c3ea4..93b3ecfc56621 100644 --- 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 @@ -27,7 +27,6 @@ internal ref partial struct TypeNameParser private Assembly _topLevelAssembly; private IList _defaultAssemblyNames; - // [RequiresUnreferencedCode("The type might be removed")] internal static Type? GetType( string typeName, Func? assemblyResolver = null, @@ -46,8 +45,7 @@ internal ref partial struct TypeNameParser { if (throwOnError) throw new TypeLoadException(SR.Arg_TypeLoadNullStr); - else - return null; + return null; } return new TypeNameParser(typeName) @@ -94,9 +92,9 @@ private bool CheckTopLevelAssemblyQualifiedName() } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "TypeNameParser.GetType is marked as RequiresUnreferencedCode.")] + Justification = "GetType APIs are marked as RequiresUnreferencedCode.")] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", - Justification = "TypeNameParser.GetType is marked as RequiresUnreferencedCode.")] + Justification = "GetType APIs are marked as RequiresUnreferencedCode.")] private Type? GetType(string typeName, ReadOnlySpan nestedTypeNames, string? assemblyNameIfAny) { Assembly? assembly; From e93980602396fb33647c0f272dca1a65bc17f5a3 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sun, 19 Mar 2023 08:49:11 -0700 Subject: [PATCH 3/9] Consistent style --- .../src/System/Reflection/TypeNameParser.CoreCLR.cs | 3 +-- .../src/System/Reflection/TypeNameParser.Mono.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) 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 index 2259d29d5e145..57bfbe3e0cb1d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs @@ -54,8 +54,7 @@ internal unsafe ref partial struct TypeNameParser { if (throwOnError) throw new TypeLoadException(SR.Arg_TypeLoadNullStr); - else - return null; + return null; } return new TypeNameParser(typeName) 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 index d41b26d1606ce..eeecbf2f9d6a9 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameParser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameParser.Mono.cs @@ -40,8 +40,7 @@ internal unsafe ref partial struct TypeNameParser { if (throwOnError) throw new TypeLoadException(SR.Arg_TypeLoadNullStr); - else - return null; + return null; } return new TypeNameParser(typeName) From e89e1dff5bcc80c321b7b255edf2dad5bad1fd44 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 20 Mar 2023 20:36:52 -0700 Subject: [PATCH 4/9] Update src/libraries/Common/src/System/Reflection/TypeNameParser.cs Co-authored-by: Aaron Robinson --- .../src/System/Reflection/TypeNameParser.cs | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/libraries/Common/src/System/Reflection/TypeNameParser.cs b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs index 6f74d05d456a9..6bbd2378cb8af 100644 --- a/src/libraries/Common/src/System/Reflection/TypeNameParser.cs +++ b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs @@ -381,25 +381,17 @@ private string GetNextAssemblyName() // private static TokenType CharToToken(char c) { - switch (c) + return c switch { - case '\0': - 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; - } + '\0' => TokenType.End, + '[' => TokenType.OpenSqBracket, + ']' => TokenType.CloseSqBracket, + ',' => TokenType.Comma, + '+' => TokenType.Plus, + '*' => TokenType.Asterisk, + '&' => TokenType.Ampersand, + _ => TokenType.Other, + }; } // From 44a91bcbeb54b0dd8d91949019c19f680b6008ca Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 20 Mar 2023 20:54:05 -0700 Subject: [PATCH 5/9] Update src/libraries/Common/src/System/Reflection/TypeNameParser.cs Co-authored-by: Aaron Robinson --- src/libraries/Common/src/System/Reflection/TypeNameParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Common/src/System/Reflection/TypeNameParser.cs b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs index 6bbd2378cb8af..cad769fe53a89 100644 --- a/src/libraries/Common/src/System/Reflection/TypeNameParser.cs +++ b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs @@ -289,7 +289,7 @@ private TokenType GetNextToken() if (token != TokenType.Other) break; src++; - if (c == '\\') + if (c == '\\') // Check for escaped character { // Update error location _errorIndex = src - 1; From fd9dc082296c4f2112cadbffb795a62c7c372f2e Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 20 Mar 2023 21:24:33 -0700 Subject: [PATCH 6/9] Feedback --- .../Reflection/TypeNameParser.CoreCLR.cs | 32 +++++++++---------- .../Reflection/TypeNameParser.NativeAot.cs | 32 +++++++++---------- src/coreclr/vm/assemblynative.cpp | 18 +++++------ src/coreclr/vm/assemblynative.hpp | 4 +-- src/coreclr/vm/typeparse.cpp | 5 ++- .../src/System/Reflection/TypeNameParser.cs | 11 ++++--- .../System/Reflection/TypeNameParser.Mono.cs | 18 +++++------ 7 files changed, 59 insertions(+), 61 deletions(-) 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 index 57bfbe3e0cb1d..a75fbe5bb32b4 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs @@ -86,7 +86,7 @@ internal unsafe ref partial struct TypeNameParser private bool CheckTopLevelAssemblyQualifiedName() { - if (_topLevelAssembly != null) + if (_topLevelAssembly is not null) { if (_throwOnError) throw new ArgumentException(SR.Argument_AssemblyGetTypeCannotSpecifyAssembly); @@ -98,10 +98,10 @@ private bool CheckTopLevelAssemblyQualifiedName() private Assembly? ResolveAssembly(string assemblyName) { Assembly? assembly; - if (_assemblyResolver != null) + if (_assemblyResolver is not null) { assembly = _assemblyResolver(new AssemblyName(assemblyName)); - if (assembly == null && _throwOnError) + if (assembly is null && _throwOnError) { throw new FileNotFoundException(SR.Format(SR.FileNotFound_ResolveAssembly, assemblyName)); } @@ -122,10 +122,10 @@ private bool CheckTopLevelAssemblyQualifiedName() { Assembly? assembly; - if (assemblyNameIfAny != null) + if (assemblyNameIfAny is not null) { assembly = ResolveAssembly(assemblyNameIfAny); - if (assembly == null) + if (assembly is null) return null; } else @@ -136,17 +136,17 @@ private bool CheckTopLevelAssemblyQualifiedName() Type? type; // Resolve the top level type. - if (_typeResolver != null) + if (_typeResolver is not null) { string escapedTypeName = EscapeTypeName(typeName); type = _typeResolver(assembly, escapedTypeName, _ignoreCase); - if (type == null) + if (type is null) { if (_throwOnError) { - throw new TypeLoadException(assembly == null ? + throw new TypeLoadException(assembly is null ? SR.Format(SR.TypeLoad_ResolveType, escapedTypeName) : SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, escapedTypeName, assembly.FullName)); } @@ -155,7 +155,7 @@ private bool CheckTopLevelAssemblyQualifiedName() } else { - if (assembly == null) + if (assembly is null) { return GetTypeFromDefaultAssemblies(typeName, nestedTypeNames); } @@ -177,7 +177,7 @@ private bool CheckTopLevelAssemblyQualifiedName() type = assembly.GetType(EscapeTypeName(typeName), throwOnError: _throwOnError, ignoreCase: _ignoreCase); } - if (type == null) + if (type is null) return null; } @@ -189,7 +189,7 @@ private bool CheckTopLevelAssemblyQualifiedName() type = type.GetNestedType(nestedTypeNames[i], bindingFlags); - if (type == null) + if (type is null) { if (_throwOnError) { @@ -206,10 +206,10 @@ private bool CheckTopLevelAssemblyQualifiedName() private Type? GetTypeFromDefaultAssemblies(string typeName, ReadOnlySpan nestedTypeNames) { RuntimeAssembly? requestingAssembly = (RuntimeAssembly?)_requestingAssembly; - if (requestingAssembly != null) + if (requestingAssembly is not null) { Type? type = ((RuntimeAssembly)requestingAssembly).GetTypeCore(typeName, nestedTypeNames, throwOnError: false, ignoreCase: _ignoreCase); - if (type != null) + if (type is not null) return type; } @@ -217,15 +217,15 @@ private bool CheckTopLevelAssemblyQualifiedName() if (requestingAssembly != coreLib) { Type? type = ((RuntimeAssembly)coreLib).GetTypeCore(typeName, nestedTypeNames, throwOnError: false, ignoreCase: _ignoreCase); - if (type != null) + if (type is not null) return type; } RuntimeAssembly? resolvedAssembly = AssemblyLoadContext.OnTypeResolve(requestingAssembly, EscapeTypeName(typeName, nestedTypeNames)); - if (resolvedAssembly != null) + if (resolvedAssembly is not null) { Type? type = resolvedAssembly.GetTypeCore(typeName, nestedTypeNames, throwOnError: false, ignoreCase: _ignoreCase); - if (type != null) + if (type is not null) return type; } 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 index 93b3ecfc56621..fa7eaf7897fec 100644 --- 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 @@ -62,7 +62,7 @@ internal ref partial struct TypeNameParser private bool CheckTopLevelAssemblyQualifiedName() { - if (_topLevelAssembly != null) + if (_topLevelAssembly is not null) { if (_throwOnError) throw new ArgumentException(SR.Argument_AssemblyGetTypeCannotSpecifyAssembly); @@ -74,7 +74,7 @@ private bool CheckTopLevelAssemblyQualifiedName() private Assembly? ResolveAssembly(string assemblyName) { Assembly? assembly; - if (_assemblyResolver != null) + if (_assemblyResolver is not null) { assembly = _assemblyResolver(new AssemblyName(assemblyName)); } @@ -83,7 +83,7 @@ private bool CheckTopLevelAssemblyQualifiedName() assembly = RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(RuntimeAssemblyName.Parse(assemblyName)); } - if (assembly == null && _throwOnError) + if (assembly is null && _throwOnError) { throw new FileNotFoundException(SR.Format(SR.FileNotFound_ResolveAssembly, assemblyName)); } @@ -99,10 +99,10 @@ private bool CheckTopLevelAssemblyQualifiedName() { Assembly? assembly; - if (assemblyNameIfAny != null) + if (assemblyNameIfAny is not null) { assembly = ResolveAssembly(assemblyNameIfAny); - if (assembly == null) + if (assembly is null) return null; } else @@ -113,17 +113,17 @@ private bool CheckTopLevelAssemblyQualifiedName() Type? type = null; // Resolve the top level type. - if (_typeResolver != null) + if (_typeResolver is not null) { string escapedTypeName = EscapeTypeName(typeName); type = _typeResolver(assembly, escapedTypeName, _ignoreCase); - if (type == null) + if (type is null) { if (_throwOnError) { - throw new TypeLoadException(assembly == null ? + throw new TypeLoadException(assembly is null ? SR.Format(SR.TypeLoad_ResolveType, escapedTypeName) : SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, escapedTypeName, assembly.FullName)); } @@ -132,7 +132,7 @@ private bool CheckTopLevelAssemblyQualifiedName() } else { - if (assembly != null) + if (assembly is not null) { if (assembly is RuntimeAssemblyInfo runtimeAssembly) { @@ -146,25 +146,25 @@ private bool CheckTopLevelAssemblyQualifiedName() type = assembly.GetType(EscapeTypeName(typeName), throwOnError: _throwOnError, ignoreCase: _ignoreCase); } - if (type == null) + if (type is null) return null; } else { - Debug.Assert(_defaultAssemblyNames != null); + Debug.Assert(_defaultAssemblyNames is not null); foreach (string defaultAssemblyName in _defaultAssemblyNames) { RuntimeAssemblyName runtimeAssemblyName = RuntimeAssemblyName.Parse(defaultAssemblyName); RuntimeAssemblyInfo defaultAssembly = RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(runtimeAssemblyName); - if (defaultAssembly == null) + if (defaultAssembly is null) continue; type = defaultAssembly.GetTypeCore(typeName, throwOnError: false, ignoreCase: _ignoreCase); - if (type != null) + if (type is not null) break; } - if (type == null) + if (type is null) { if (_throwOnError) { @@ -189,7 +189,7 @@ private bool CheckTopLevelAssemblyQualifiedName() type = type.GetNestedType(nestedTypeNames[i], bindingFlags); // Compat: Non-extensible parser allows ambiguous matches with ignore case lookup - if (type == null && _ignoreCase && !_extensibleParser) + 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(); @@ -203,7 +203,7 @@ private bool CheckTopLevelAssemblyQualifiedName() } } - if (type == null) + if (type is null) { if (_throwOnError) { diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index 4eaeac5e12dbb..eac1381922c53 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -338,7 +338,7 @@ extern "C" void QCALLTYPE AssemblyNative_GetLocation(QCall::AssemblyHandle pAsse extern "C" void QCALLTYPE AssemblyNative_GetTypeCore(QCall::AssemblyHandle assemblyHandle, LPCSTR szTypeName, LPCSTR * rgszNestedTypeNames, - INT32 cNestedTypeNamesLength, + int32_t cNestedTypeNamesLength, QCall::ObjectHandleOnStack retType) { CONTRACTL @@ -358,7 +358,7 @@ extern "C" void QCALLTYPE AssemblyNative_GetTypeCore(QCall::AssemblyHandle assem NameHandle typeName(pManifestModule, mdtBaseType); - for (INT32 i = -1; i < cNestedTypeNamesLength; i++) + for (int32_t i = -1; i < cNestedTypeNamesLength; i++) { typeName.SetName((i == -1) ? szTypeName : rgszNestedTypeNames[i]); @@ -371,9 +371,8 @@ extern "C" void QCALLTYPE AssemblyNative_GetTypeCore(QCall::AssemblyHandle assem 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(); @@ -392,7 +391,7 @@ extern "C" void QCALLTYPE AssemblyNative_GetTypeCore(QCall::AssemblyHandle assem extern "C" void QCALLTYPE AssemblyNative_GetTypeCoreIgnoreCase(QCall::AssemblyHandle assemblyHandle, LPCWSTR wszTypeName, LPCWSTR* rgwszNestedTypeNames, - INT32 cNestedTypeNamesLength, + int32_t cNestedTypeNamesLength, QCall::ObjectHandleOnStack retType) { CONTRACTL @@ -415,7 +414,7 @@ extern "C" void QCALLTYPE AssemblyNative_GetTypeCoreIgnoreCase(QCall::AssemblyHa // Set up the name handle typeName.SetCaseInsensitive(); - for (INT32 i = -1; i < cNestedTypeNamesLength; i++) + for (int32_t i = -1; i < cNestedTypeNamesLength; i++) { // each extra name represents one more level of nesting StackSString name((i == -1) ? wszTypeName : rgwszNestedTypeNames[i]); @@ -434,9 +433,8 @@ extern "C" void QCALLTYPE AssemblyNative_GetTypeCoreIgnoreCase(QCall::AssemblyHa 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(); diff --git a/src/coreclr/vm/assemblynative.hpp b/src/coreclr/vm/assemblynative.hpp index 096848f73650d..5c08c7a8edb31 100644 --- a/src/coreclr/vm/assemblynative.hpp +++ b/src/coreclr/vm/assemblynative.hpp @@ -80,10 +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_GetTypeCore(QCall::AssemblyHandle pAssembly, LPCSTR szTypeName, LPCSTR* rgszNestedTypeNames, INT32 cNestedTypeNamesLength, QCall::ObjectHandleOnStack retType); +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 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/typeparse.cpp b/src/coreclr/vm/typeparse.cpp index 397dea076ee6d..3e06633a65f44 100644 --- a/src/coreclr/vm/typeparse.cpp +++ b/src/coreclr/vm/typeparse.cpp @@ -1080,9 +1080,8 @@ TypeName::GetTypeHaveAssembly( 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(); diff --git a/src/libraries/Common/src/System/Reflection/TypeNameParser.cs b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs index cad769fe53a89..ba542f65b60c5 100644 --- a/src/libraries/Common/src/System/Reflection/TypeNameParser.cs +++ b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs @@ -447,8 +447,6 @@ private sealed class AssemblyQualifiedTypeName : TypeName public AssemblyQualifiedTypeName(TypeName nonQualifiedTypeName, string assemblyName) { - Debug.Assert(nonQualifiedTypeName != null); - Debug.Assert(assemblyName != null); _nonQualifiedTypeName = nonQualifiedTypeName; _assemblyName = assemblyName; } @@ -505,11 +503,14 @@ public NestedNamespaceTypeName(string fullName, string[] nestedNames, int nested 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 = 0; - public const int Pointer = -1; - public const int ByRef = -2; + public const int Array = -1; + public const int Pointer = -2; + public const int ByRef = -3; public ModifierTypeName(TypeName elementTypeName, int rankOrModifier) { 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 index eeecbf2f9d6a9..d55385462faaf 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameParser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameParser.Mono.cs @@ -63,10 +63,10 @@ private static bool CheckTopLevelAssemblyQualifiedName() var name = new AssemblyName(assemblyName); Assembly? assembly; - if (_assemblyResolver != null) + if (_assemblyResolver is not null) { assembly = _assemblyResolver(name); - if (assembly == null && _throwOnError) + if (assembly is null && _throwOnError) { throw new FileNotFoundException(SR.Format(SR.FileNotFound_ResolveAssembly, assemblyName)); } @@ -102,7 +102,7 @@ private static bool CheckTopLevelAssemblyQualifiedName() Justification = "TypeNameParser.GetType is marked as RequiresUnreferencedCode.")] private Type? GetType(string typeName, ReadOnlySpan nestedTypeNames, string? assemblyNameIfAny) { - Assembly? assembly = (assemblyNameIfAny != null) ? ResolveAssembly(assemblyNameIfAny) : null; + 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); @@ -110,15 +110,15 @@ private static bool CheckTopLevelAssemblyQualifiedName() Type? type; // Resolve the top level type. - if (_typeResolver != null) + if (_typeResolver is not null) { type = _typeResolver(assembly, escapedTypeName, _ignoreCase); - if (type == null) + if (type is null) { if (_throwOnError) { - throw new TypeLoadException(assembly == null ? + throw new TypeLoadException(assembly is null ? SR.Format(SR.TypeLoad_ResolveType, escapedTypeName) : SR.Format(SR.TypeLoad_ResolveTypeFromAssembly, escapedTypeName, assembly.FullName)); } @@ -127,7 +127,7 @@ private static bool CheckTopLevelAssemblyQualifiedName() } else { - if (assembly == null) + if (assembly is null) { ref StackCrawlMark stackMark = ref Unsafe.AsRef(_stackMark); @@ -138,7 +138,7 @@ private static bool CheckTopLevelAssemblyQualifiedName() type = assembly.GetType(escapedTypeName, _throwOnError, _ignoreCase); } - if (type == null) + if (type is null) { return null; } @@ -152,7 +152,7 @@ private static bool CheckTopLevelAssemblyQualifiedName() type = type.GetNestedType(nestedTypeNames[i], bindingFlags); - if (type == null) + if (type is null) { if (_throwOnError) { From 179ccb044805898d56385be3a90b750e3eff71a7 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 21 Mar 2023 10:44:26 -0700 Subject: [PATCH 7/9] Move shared fields to shared file --- .../src/System/Reflection/TypeNameParser.CoreCLR.cs | 4 ---- .../src/System/Reflection/TypeNameParser.NativeAot.cs | 4 ---- .../Common/src/System/Reflection/TypeNameParser.cs | 6 ++++++ .../src/System/Reflection/TypeNameParser.Mono.cs | 4 ---- 4 files changed, 6 insertions(+), 12 deletions(-) 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 index a75fbe5bb32b4..17a9bf1ba37d6 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameParser.CoreCLR.cs @@ -13,10 +13,6 @@ namespace System.Reflection { internal unsafe ref partial struct TypeNameParser { - private ReadOnlySpan _input; - private int _index; - private int _errorIndex; // Position for error reporting - private Func? _assemblyResolver; private Func? _typeResolver; private bool _throwOnError; 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 index fa7eaf7897fec..154cfea110f15 100644 --- 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 @@ -15,10 +15,6 @@ namespace System.Reflection // internal ref partial struct TypeNameParser { - private ReadOnlySpan _input; - private int _index; - private int _errorIndex; // Position for error reporting - private Func? _assemblyResolver; private Func? _typeResolver; private bool _throwOnError; diff --git a/src/libraries/Common/src/System/Reflection/TypeNameParser.cs b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs index ba542f65b60c5..85c66189da23e 100644 --- a/src/libraries/Common/src/System/Reflection/TypeNameParser.cs +++ b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; using System.Text; namespace System.Reflection @@ -10,8 +11,13 @@ 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; 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 index d55385462faaf..3622b4e27626a 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameParser.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameParser.Mono.cs @@ -13,10 +13,6 @@ namespace System.Reflection { internal unsafe ref partial struct TypeNameParser { - private ReadOnlySpan _input; - private int _index; - private int _errorIndex; // Position for error reporting - private Func? _assemblyResolver; private Func? _typeResolver; private bool _throwOnError; From 0d90b3c6eb45fb2da01702873ddce6b372858711 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 24 Mar 2023 13:08:27 -0700 Subject: [PATCH 8/9] Feedback --- .../Reflection/Core/AssemblyBinder.cs | 2 - .../Core/Execution/ExecutionDomain.cs | 4 +- .../Reflection/TypeNameParser.NativeAot.cs | 58 ++++++++++++------- ...nExecutionDomainCallbacksImplementation.cs | 6 +- .../src/System/Reflection/TypeNameParser.cs | 4 +- 5 files changed, 42 insertions(+), 32 deletions(-) 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 312d44bbe6f52..64f5db3338bb5 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 @@ -38,7 +38,7 @@ internal ExecutionDomain(ReflectionDomainSetup executionDomainSetup, ExecutionEn // // Retrieves a type by name. Helper to implement Type.GetType(); // - public Type GetType(string typeName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase, bool extensibleParser, IList defaultAssemblyNames) + public Type GetType(string typeName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase, bool extensibleParser, string defaultAssemblyName) { return TypeNameParser.GetType( typeName, @@ -47,7 +47,7 @@ public Type GetType(string typeName, Func assemblyResolv throwOnError: throwOnError, ignoreCase: ignoreCase, extensibleParser: extensibleParser, - defaultAssemblyNames: defaultAssemblyNames); + defaultAssemblyName: defaultAssemblyName); } // 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 index 154cfea110f15..d3f7d9ebef914 100644 --- 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 @@ -20,18 +20,17 @@ internal ref partial struct TypeNameParser private bool _throwOnError; private bool _ignoreCase; private bool _extensibleParser; - private Assembly _topLevelAssembly; - private IList _defaultAssemblyNames; + private Assembly? _topLevelAssembly; + private string? _defaultAssemblyName; internal static Type? GetType( string typeName, - Func? assemblyResolver = null, - Func? typeResolver = null, + Func? assemblyResolver, + Func? typeResolver, bool throwOnError = false, bool ignoreCase = false, - bool extensibleParser = false, - Assembly topLevelAssembly = null, - IList defaultAssemblyNames = null) + bool extensibleParser = true, + string? defaultAssemblyName = null) { ArgumentNullException.ThrowIfNull(typeName); @@ -51,8 +50,21 @@ internal ref partial struct TypeNameParser _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, - _defaultAssemblyNames = defaultAssemblyNames }.Parse(); } @@ -147,27 +159,31 @@ private bool CheckTopLevelAssemblyQualifiedName() } else { - Debug.Assert(_defaultAssemblyNames is not null); + RuntimeAssemblyInfo? defaultAssembly = null; + if (_defaultAssemblyName != null) + { + defaultAssembly = RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(RuntimeAssemblyName.Parse(_defaultAssemblyName)); + if (defaultAssembly != null) + { + type = defaultAssembly.GetTypeCore(typeName, throwOnError: false, ignoreCase: _ignoreCase); + } + } - foreach (string defaultAssemblyName in _defaultAssemblyNames) + RuntimeAssemblyInfo? coreLib = null; + if (type is null) { - RuntimeAssemblyName runtimeAssemblyName = RuntimeAssemblyName.Parse(defaultAssemblyName); - RuntimeAssemblyInfo defaultAssembly = RuntimeAssemblyInfo.GetRuntimeAssemblyIfExists(runtimeAssemblyName); - if (defaultAssembly is null) - continue; - type = defaultAssembly.GetTypeCore(typeName, throwOnError: false, ignoreCase: _ignoreCase); - if (type is not null) - break; + coreLib = (RuntimeAssemblyInfo)typeof(object).Assembly; + if (coreLib != assembly) + { + type = coreLib.GetTypeCore(typeName, throwOnError: false, ignoreCase: _ignoreCase); + } } if (type is null) { if (_throwOnError) { - if (_defaultAssemblyNames.Count > 0) - throw Helpers.CreateTypeLoadException(typeName, _defaultAssemblyNames[0]); - else - throw new TypeLoadException(SR.Format(SR.TypeLoad_TypeNotFound, typeName)); + throw Helpers.CreateTypeLoadException(typeName, (defaultAssembly ?? coreLib).FullName); } return 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 a7f0ee2a1ac59..7a3dc27d46594 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 @@ -33,11 +33,7 @@ public ReflectionExecutionDomainCallbacksImplementation(ExecutionDomain executio public sealed override Type GetType(string typeName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase, bool extensibleParser, string defaultAssemblyName) { - LowLevelListWithIList defaultAssemblies = new LowLevelListWithIList(); - if (defaultAssemblyName != null) - defaultAssemblies.Add(defaultAssemblyName); - defaultAssemblies.Add(AssemblyBinder.DefaultAssemblyNameForGetType); - return _executionDomain.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, extensibleParser, defaultAssemblies); + return _executionDomain.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, extensibleParser, defaultAssemblyName); } public sealed override bool IsReflectionBlocked(RuntimeTypeHandle typeHandle) diff --git a/src/libraries/Common/src/System/Reflection/TypeNameParser.cs b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs index 85c66189da23e..9dcdf11079d8d 100644 --- a/src/libraries/Common/src/System/Reflection/TypeNameParser.cs +++ b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs @@ -504,7 +504,7 @@ public NestedNamespaceTypeName(string fullName, string[] nestedNames, int nested } // - // Abstract base for array, byref and pointer type names. + // Array, byref or pointer type name. // private sealed class ModifierTypeName : TypeName { @@ -543,7 +543,7 @@ public ModifierTypeName(TypeName elementTypeName, int rankOrModifier) } // - // A constructed generic type. + // Constructed generic type name. // private sealed class GenericTypeName : TypeName { From 9f897976db45c33e6b9ca74b03468f248dced4d6 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 24 Mar 2023 14:15:52 -0700 Subject: [PATCH 9/9] Remove unnecessary abstraction layers --- .../Reflection/Core/Execution/ExecutionDomain.cs | 15 --------------- .../ReflectionExecutionDomainCallbacks.cs | 3 --- .../Runtime/CompilerHelpers/ReflectionHelpers.cs | 7 +++---- .../System/Reflection/TypeNameParser.NativeAot.cs | 10 ++++++++++ .../src/System/Type.NativeAot.cs | 10 ++++++++-- ...ctionExecutionDomainCallbacksImplementation.cs | 1 - ...ctionExecutionDomainCallbacksImplementation.cs | 5 ----- 7 files changed, 21 insertions(+), 30 deletions(-) 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 64f5db3338bb5..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 @@ -35,21 +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, bool extensibleParser, string defaultAssemblyName) - { - return TypeNameParser.GetType( - typeName, - assemblyResolver: assemblyResolver, - typeResolver: typeResolver, - throwOnError: throwOnError, - ignoreCase: ignoreCase, - extensibleParser: extensibleParser, - defaultAssemblyName: defaultAssemblyName); - } - // // 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 f4e9115121682..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, bool extensibleParser, 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 14f2f253f833e..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,16 +18,14 @@ internal static class ReflectionHelpers // a default assembly name. public static Type GetType(string typeName, string callingAssemblyName, bool throwOnError, bool ignoreCase) { - return RuntimeAugments.Callbacks.GetType(typeName, null, null, - throwOnError: throwOnError, ignoreCase: ignoreCase, extensibleParser: false, callingAssemblyName); + 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: throwOnError, ignoreCase: ignoreCase, extensibleParser: true, 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/Reflection/TypeNameParser.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/TypeNameParser.NativeAot.cs index d3f7d9ebef914..ea3c7f89b7104 100644 --- 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 @@ -23,6 +23,16 @@ internal ref partial struct TypeNameParser 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, 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 e8df778ded4e3..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) => RuntimeAugments.Callbacks.GetType(typeName, null, null, throwOnError: throwOnError, ignoreCase: ignoreCase, extensibleParser: false, defaultAssembly: null); + 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, extensibleParser: true, 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 3985797c3938c..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, bool extensibleParser, 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/ReflectionExecutionDomainCallbacksImplementation.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ReflectionExecutionDomainCallbacksImplementation.cs index 7a3dc27d46594..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,11 +31,6 @@ public ReflectionExecutionDomainCallbacksImplementation(ExecutionDomain executio _executionEnvironment = executionEnvironment; } - public sealed override Type GetType(string typeName, Func assemblyResolver, Func typeResolver, bool throwOnError, bool ignoreCase, bool extensibleParser, string defaultAssemblyName) - { - return _executionDomain.GetType(typeName, assemblyResolver, typeResolver, throwOnError, ignoreCase, extensibleParser, defaultAssemblyName); - } - public sealed override bool IsReflectionBlocked(RuntimeTypeHandle typeHandle) { return _executionEnvironment.IsReflectionBlocked(typeHandle);