Skip to content

Commit

Permalink
Fix Type.GetType for global type names with leading '.'
Browse files Browse the repository at this point in the history
Ignore leading '.' for global typenames for compatibility with earlier .NET versions.

Fixes #84644
  • Loading branch information
jkotas committed Apr 18, 2023
1 parent add51b8 commit b5b40d1
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/libraries/Common/src/System/Reflection/TypeNameParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ private TypeNameParser(ReadOnlySpan<char> name)
if (fullName is null)
return null;

fullName = ApplyLeadingDotCompatQuirk(fullName);

if (Peek == TokenType.Plus)
{
string[] nestedNames = new string[1];
Expand All @@ -180,6 +182,8 @@ private TypeNameParser(ReadOnlySpan<char> name)
if (nestedName is null)
return null;

nestedName = ApplyLeadingDotCompatQuirk(nestedName);

if (nestedNamesCount >= nestedNames.Length)
Array.Resize(ref nestedNames, 2 * nestedNamesCount);
nestedNames[nestedNamesCount++] = nestedName;
Expand All @@ -192,6 +196,19 @@ private TypeNameParser(ReadOnlySpan<char> name)
{
return new NamespaceTypeName(fullName);
}

// Compat: Ignore leading '.' for type names without namespace. .NET Framework historically ignored leading '.' here. It is likely
// that code out there depends on this behavior. For example, type names formed by concatenating namespace and name, without checking for
// empty namespace (bug), are going to have superfluous leading '.'.
// This behavior means that types that start with '.' are not round-trippable via type name.
static string ApplyLeadingDotCompatQuirk(string typeName)
{
#if NETCOREAPP
return (typeName.StartsWith('.') && !typeName.AsSpan(1).Contains('.')) ? typeName.Substring(1) : typeName;
#else
return ((typeName.Length > 0) && (typeName[0] == '.') && typeName.LastIndexOf('.') == 0) ? typeName.Substring(1) : typeName;
#endif
}
}

//
Expand Down
14 changes: 14 additions & 0 deletions src/libraries/System.Runtime/tests/System/Type/TypeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,8 @@ public void MakePointerType_ByRef_ThrowsTypeLoadException()
[InlineData("Outside[,,]", typeof(Outside[,,]))]
[InlineData("Outside[][]", typeof(Outside[][]))]
[InlineData("Outside`1[System.Nullable`1[System.Boolean]]", typeof(Outside<bool?>))]
[InlineData(".Outside`1", typeof(Outside<>))]
[InlineData(".Outside`1+.Inside`1", typeof(Outside<>.Inside<>))]
public void GetTypeByName_ValidType_ReturnsExpected(string typeName, Type expectedType)
{
Assert.Equal(expectedType, Type.GetType(typeName, throwOnError: false, ignoreCase: false));
Expand All @@ -520,6 +522,8 @@ public void GetTypeByName_ValidType_ReturnsExpected(string typeName, Type expect
[InlineData("System.Int32[,*,]", typeof(ArgumentException), false)]
[InlineData("Outside`2", typeof(TypeLoadException), false)]
[InlineData("Outside`1[System.Boolean, System.Int32]", typeof(ArgumentException), true)]
[InlineData(".System.Int32", typeof(TypeLoadException), false)]
[InlineData("..Outside`1", typeof(TypeLoadException), false)]
public void GetTypeByName_Invalid(string typeName, Type expectedException, bool alwaysThrowsException)
{
if (!alwaysThrowsException)
Expand All @@ -530,6 +534,16 @@ public void GetTypeByName_Invalid(string typeName, Type expectedException, bool
Assert.Throws(expectedException, () => Type.GetType(typeName, throwOnError: true, ignoreCase: false));
}

[Theory]
[InlineData(".GlobalStructStartingWithDot")]
[InlineData(" GlobalStructStartingWithSpace")]
public void GetTypeByName_NonRountripable(string typeName)
{
Type type = Assembly.Load("System.TestStructs").GetTypes().Single((t) => t.FullName == typeName);
string assemblyQualifiedName = type.AssemblyQualifiedName;
Assert.Null(Type.GetType(assemblyQualifiedName));
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtimelab/issues/155", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/52393", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,13 @@
}
}
}

.class public sequential sealed '.GlobalStructStartingWithDot'
extends [System.Runtime]System.ValueType
{
}

.class public sequential sealed ' GlobalStructStartingWithSpace'
extends [System.Runtime]System.ValueType
{
}

0 comments on commit b5b40d1

Please sign in to comment.