From 3883c362bde720782a33f221b1bc37088723f8d5 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 30 Mar 2023 10:27:51 -0700 Subject: [PATCH] Fix type parsing error returned for type names with invalid start of assembly name Fixes #84118 --- .../src/System/Reflection/TypeNameParser.cs | 24 +++++++++++++++---- .../System.Reflection/tests/GetTypeTests.cs | 9 +++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/libraries/Common/src/System/Reflection/TypeNameParser.cs b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs index 9dcdf11079d8d..77917942d49c6 100644 --- a/src/libraries/Common/src/System/Reflection/TypeNameParser.cs +++ b/src/libraries/Common/src/System/Reflection/TypeNameParser.cs @@ -48,6 +48,8 @@ private TypeNameParser(ReadOnlySpan name) return null; assemblyName = GetNextAssemblyName(); + if (assemblyName is null) + return null; Debug.Assert(Peek == TokenType.End); } @@ -126,7 +128,7 @@ private TypeNameParser(ReadOnlySpan name) 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))) + if (!(Peek is TokenType.OpenSqBracket && (PeekSecond is TokenType.Other or TokenType.OpenSqBracket))) return namedType; Skip(); @@ -328,9 +330,10 @@ private TokenType GetNextToken() // 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() + private string? GetNextAssemblyName() { - SkipWhiteSpace(); + if (!StartAssemblyName()) + return null; string assemblyName = new string(_input.Slice(_index)); _index = _input.Length; @@ -344,7 +347,8 @@ private string GetNextAssemblyName() // private string? GetNextEmbeddedAssemblyName() { - SkipWhiteSpace(); + if (!StartAssemblyName()) + return null; ValueStringBuilder sb = new ValueStringBuilder(stackalloc char[64]); @@ -382,6 +386,18 @@ private string GetNextAssemblyName() return sb.ToString(); } + private bool StartAssemblyName() + { + // Compat: Treat invalid starting token of assembly name as type name parsing error instead of assembly name parsing error. This only affects + // exception returned by the parser. + if (Peek is TokenType.End or TokenType.Comma) + { + ParseError(); + return false; + } + return true; + } + // // Classify a character as a TokenType. (Fortunately, all tokens in type name strings other than identifiers are single-character tokens.) // diff --git a/src/libraries/System.Reflection/tests/GetTypeTests.cs b/src/libraries/System.Reflection/tests/GetTypeTests.cs index 43820b36351d8..01a04df7be652 100644 --- a/src/libraries/System.Reflection/tests/GetTypeTests.cs +++ b/src/libraries/System.Reflection/tests/GetTypeTests.cs @@ -269,6 +269,15 @@ public void GetType_GenericTypeArgumentList() Assert.Equal(typeof(System.Reflection.Tests.GenericClass), Type.GetType("System.Reflection.Tests.GenericClass`1[[System.String, System.Private.CoreLib]]", throwOnError: true)); Assert.Throws(() => Type.GetType("System.Reflection.Tests.GenericClass`1[[Bogus, BogusAssembly]]", throwOnError: true)); } + + [Fact] + public void GetType_InvalidAssemblyName() + { + Assert.Null(Type.GetType("MissingAssemblyName, ")); + Assert.Null(Type.GetType("ExtraComma, ,")); + Assert.Null(Type.GetType("ExtraComma, , System.Runtime")); + Assert.Throws(() => Type.GetType("System.Object, System.Runtime, Version=x.y")); + } } namespace MyNamespace1