summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/TypeNameParser.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/TypeNameParser.cs')
-rw-r--r--src/mscorlib/src/System/TypeNameParser.cs365
1 files changed, 365 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/TypeNameParser.cs b/src/mscorlib/src/System/TypeNameParser.cs
new file mode 100644
index 0000000000..fee0f3aa64
--- /dev/null
+++ b/src/mscorlib/src/System/TypeNameParser.cs
@@ -0,0 +1,365 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+using System;
+using System.Diagnostics.Contracts;
+using System.IO;
+using System.Reflection;
+using System.Security;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Runtime.Versioning;
+using Microsoft.Win32.SafeHandles;
+
+namespace System
+{
+ [SecurityCritical]
+ internal class SafeTypeNameParserHandle : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ #region QCalls
+ [SecurityCritical]
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ private static extern void _ReleaseTypeNameParser(IntPtr pTypeNameParser);
+ #endregion
+
+ public SafeTypeNameParserHandle()
+ : base(true)
+ {
+ }
+
+ [SecurityCritical]
+ protected override bool ReleaseHandle()
+ {
+ _ReleaseTypeNameParser(handle);
+ handle = IntPtr.Zero;
+ return true;
+ }
+ }
+
+ internal sealed class TypeNameParser : IDisposable
+ {
+ #region QCalls
+ [SecurityCritical]
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ private static extern void _CreateTypeNameParser(string typeName, ObjectHandleOnStack retHandle, bool throwOnError);
+
+ [SecurityCritical]
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ private static extern void _GetNames(SafeTypeNameParserHandle pTypeNameParser, ObjectHandleOnStack retArray);
+
+ [SecurityCritical]
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ private static extern void _GetTypeArguments(SafeTypeNameParserHandle pTypeNameParser, ObjectHandleOnStack retArray);
+
+ [SecurityCritical]
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ private static extern void _GetModifiers(SafeTypeNameParserHandle pTypeNameParser, ObjectHandleOnStack retArray);
+
+ [SecurityCritical]
+ [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
+ [SuppressUnmanagedCodeSecurity]
+ private static extern void _GetAssemblyName(SafeTypeNameParserHandle pTypeNameParser, StringHandleOnStack retString);
+ #endregion
+
+ #region Static Members
+ [SecuritySafeCritical]
+ internal static Type GetType(
+ string typeName,
+ Func<AssemblyName, Assembly> assemblyResolver,
+ Func<Assembly, string, bool, Type> typeResolver,
+ bool throwOnError,
+ bool ignoreCase,
+ ref StackCrawlMark stackMark)
+ {
+ if (typeName == null)
+ throw new ArgumentNullException("typeName");
+ if (typeName.Length > 0 && typeName[0] == '\0')
+ throw new ArgumentException(Environment.GetResourceString("Format_StringZeroLength"));
+ Contract.EndContractBlock();
+
+ 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
+ [SecurityCritical]
+ private SafeTypeNameParserHandle m_NativeParser;
+ private static readonly char[] SPECIAL_CHARS = {',', '[', ']', '&', '*', '+', '\\'}; /* see typeparse.h */
+ #endregion
+
+ #region Constructor and Disposer
+ [SecuritySafeCritical]
+ private TypeNameParser(SafeTypeNameParserHandle handle)
+ {
+ m_NativeParser = handle;
+ }
+
+ [SecuritySafeCritical]
+ public void Dispose()
+ {
+ m_NativeParser.Dispose();
+ }
+ #endregion
+
+ #region private Members
+ [SecuritySafeCritical]
+ private unsafe Type ConstructType(
+ Func<AssemblyName, Assembly> assemblyResolver,
+ Func<Assembly, string, bool, Type> typeResolver,
+ bool throwOnError,
+ bool ignoreCase,
+ ref StackCrawlMark stackMark)
+ {
+ // assembly name
+ Assembly assembly = null;
+ string asmName = GetAssemblyName();
+
+ // GetAssemblyName never returns null
+ Contract.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(Environment.GetResourceString("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.
+ Contract.Assert(throwOnError == false);
+ return null;
+ }
+
+ SafeTypeNameParserHandle[] typeArguments = GetTypeArguments();
+
+ Type[] types = null;
+ if (typeArguments != null)
+ {
+ types = new Type[typeArguments.Length];
+ for (int i = 0; i < typeArguments.Length; i++)
+ {
+ Contract.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.
+ Contract.Assert(throwOnError == false);
+ return null;
+ }
+ }
+ }
+
+ int[] modifiers = GetModifiers();
+
+ fixed (int* ptr = modifiers)
+ {
+ IntPtr intPtr = new IntPtr(ptr);
+ return RuntimeTypeHandle.GetTypeHelper(baseType, types, intPtr, modifiers == null ? 0 : modifiers.Length);
+ }
+ }
+
+ [SecuritySafeCritical]
+ private static Assembly ResolveAssembly(string asmName, Func<AssemblyName, Assembly> assemblyResolver, bool throwOnError, ref StackCrawlMark stackMark)
+ {
+ Contract.Requires(asmName != null && asmName.Length > 0);
+
+ Assembly assembly = null;
+
+ if (assemblyResolver == null)
+ {
+ if (throwOnError)
+ {
+ assembly = RuntimeAssembly.InternalLoad(asmName, null, ref stackMark, false /*forIntrospection*/);
+ }
+ else
+ {
+ // When throwOnError is false we should only catch FileNotFoundException.
+ // Other exceptions like BadImangeFormatException should still fly.
+ try
+ {
+ assembly = RuntimeAssembly.InternalLoad(asmName, null, ref stackMark, false /*forIntrospection*/);
+ }
+ catch (FileNotFoundException)
+ {
+ return null;
+ }
+ }
+ }
+ else
+ {
+ assembly = assemblyResolver(new AssemblyName(asmName));
+ if (assembly == null && throwOnError)
+ {
+ throw new FileNotFoundException(Environment.GetResourceString("FileNotFound_ResolveAssembly", asmName));
+ }
+ }
+
+ return assembly;
+ }
+
+ private static Type ResolveType(Assembly assembly, string[] names, Func<Assembly, string, bool, Type> typeResolver, bool throwOnError, bool ignoreCase, ref StackCrawlMark stackMark)
+ {
+ Contract.Requires(names != null && names.Length > 0);
+
+ Type type = null;
+
+ // 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 ?
+ Environment.GetResourceString("TypeLoad_ResolveType", OuterMostTypeName) :
+ Environment.GetResourceString("TypeLoad_ResolveTypeFromAssembly", OuterMostTypeName, assembly.FullName);
+
+ throw new TypeLoadException(errorString);
+ }
+ }
+ else
+ {
+ if (assembly == null)
+ {
+ type = RuntimeType.GetType(OuterMostTypeName, throwOnError, ignoreCase, false, 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(Environment.GetResourceString("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;
+
+ StringBuilder sb = StringBuilderCache.Acquire();
+ foreach (char c in name)
+ {
+ if (Array.IndexOf<char>(SPECIAL_CHARS, c) >= 0)
+ sb.Append('\\');
+
+ sb.Append(c);
+ }
+
+ return StringBuilderCache.GetStringAndRelease(sb);
+ }
+
+ [SecuritySafeCritical]
+ private static SafeTypeNameParserHandle CreateTypeNameParser(string typeName, bool throwOnError)
+ {
+ SafeTypeNameParserHandle retHandle = null;
+ _CreateTypeNameParser(typeName, JitHelpers.GetObjectHandleOnStack(ref retHandle), throwOnError);
+
+ return retHandle;
+ }
+
+ [SecuritySafeCritical]
+ private string[] GetNames()
+ {
+ string[] names = null;
+ _GetNames(m_NativeParser, JitHelpers.GetObjectHandleOnStack(ref names));
+
+ return names;
+ }
+
+ [SecuritySafeCritical]
+ private SafeTypeNameParserHandle[] GetTypeArguments()
+ {
+ SafeTypeNameParserHandle[] arguments = null;
+ _GetTypeArguments(m_NativeParser, JitHelpers.GetObjectHandleOnStack(ref arguments));
+
+ return arguments;
+ }
+
+ [SecuritySafeCritical]
+ private int[] GetModifiers()
+ {
+ int[] modifiers = null;
+ _GetModifiers(m_NativeParser, JitHelpers.GetObjectHandleOnStack(ref modifiers));
+
+ return modifiers;
+ }
+
+ [SecuritySafeCritical]
+ private string GetAssemblyName()
+ {
+ string assemblyName = null;
+ _GetAssemblyName(m_NativeParser, JitHelpers.GetStringHandleOnStack(ref assemblyName));
+
+ return assemblyName;
+ }
+ #endregion
+ }
+}