Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
}

[ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBuiltWithAggressiveTrimming))]
[InlineData(".GlobalStructStartingWithDot")]
[InlineData(" GlobalStructStartingWithSpace")]
public void GetTypeByName_NonRoundtrippable(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
{
}