// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #pragma warning disable CA2208 using System.Collections.Generic; namespace System.Reflection.Runtime.TypeParsing { // // Parser for type names passed to GetType() apis. // public sealed class TypeParser { // // Parses a typename. The typename may be optionally postpended with a "," followed by a legal assembly name. // public static TypeName? ParseTypeName(string 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) { if (string.IsNullOrEmpty(s)) return null; // 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(); 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(); List<TypeName> genericTypeArguments = new List<TypeName>(); 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(); string[] parts = fullName.Split('.'); int numNamespaceParts = parts.Length - 1; string[] namespaceParts = new string[numNamespaceParts]; for (int i = 0; i < numNamespaceParts; i++) namespaceParts[numNamespaceParts - i - 1] = parts[i]; string name = parts[numNamespaceParts]; return new NamespaceTypeName(namespaceParts, name); } // // Parse a generic argument. In particular, generic arguments can take the special form [<typename>,<assemblyname>]. // 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; } } |