-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add TypeName
APIs to simplify metadata lookup.
#111598
base: main
Are you sure you want to change the base?
Add TypeName
APIs to simplify metadata lookup.
#111598
Conversation
Note regarding the
|
Note regarding the
|
...libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserHelpers.cs
Outdated
Show resolved
Hide resolved
Could you please delete runtime/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems Lines 1492 to 1494 in 29013d8
(Other uses of TypeNameHelpers.cs should be delete too once these APIs are available in the LKG runtime.) |
...libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserHelpers.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeName.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameTests.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 5 out of 17 changed files in this pull request and generated no comments.
Files not reviewed (12)
- src/coreclr/tools/ILVerification/ILVerification.projitems: Language not supported
- src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj: Language not supported
- src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj: Language not supported
- src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems: Language not supported
- src/tools/illink/src/linker/Mono.Linker.csproj: Language not supported
- src/libraries/Common/src/System/Reflection/Metadata/TypeNameHelpers.cs: Evaluated as low risk
- src/coreclr/tools/Common/TypeSystem/Common/Utilities/CustomAttributeTypeNameParser.cs: Evaluated as low risk
- src/tools/illink/src/linker/Linker/TypeNameResolver.cs: Evaluated as low risk
- src/mono/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.Mono.cs: Evaluated as low risk
- src/libraries/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.cs: Evaluated as low risk
- src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.cs: Evaluated as low risk
- src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.NativeAot.cs: Evaluated as low risk
e1a9730
to
d08be6b
Compare
d08be6b
to
1e3f969
Compare
We fail to parse Lines 253 to 256 in 843f798
|
I think we should match the .NET Framework behavior for these corner cases. For example:
failed in .NET Framework. It "works" in .NET Core today, but I do not think that it should. I would make it fail again and update the tests as necessary. |
...libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeNameParserHelpers.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameTests.cs
Show resolved
Hide resolved
Updated tests to account for |
Could you please take a look at the Mono test failures? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. @adamsitnik Could you please review this change as well? We are changing the type name parsing behavior for nested types with its own namespaces to fix .NET Framework -> .NET Core type name parsing regression and to better match reflection behavior.
|
Mono thinks runtime/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs Lines 1011 to 1018 in 8eab62c
|
Looks like something that should be cleaned up. Do you have a good idea what it needs to be done? |
I tried something. Let's see. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thank you for your contribution @teo-tsirpanis !
And big thanks to @jkotas for the review!
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeName.cs
Show resolved
Hide resolved
// It always takes O(m * i) worst-case time and is safe to use here. | ||
|
||
int offset = fullName.LastIndexOfAny('.', '+'); | ||
int offset = fullName.LastIndexOf('.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to Copilot, IL allows for creating a type with a name that contains a dot that is not treated as a namespace separator. Example: Namespace.Type.Name
I've tried to repro it with ILEmit, but it treats dots as namespace separators:
using System.Reflection;
using System.Reflection.Emit;
AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("AssemblyName"), AssemblyBuilderAccess.Run);
ModuleBuilder module = assembly.DefineDynamicModule("ModuleName");
Type typeWithDot = module.DefineType("Namespace.Type.Name").CreateType();
Console.WriteLine($"FullName: {typeWithDot.FullName}");
Console.WriteLine($"Namespace: {typeWithDot.Namespace}");
FullName: Namespace.Type.Name
Namespace: Namespace.Type
Of course I am not trying to say that we should change the implementation. I just wonder if it's worth adding a remark in the xml docs? Or at least a comment (that would also mention that we don't need to worry about escape characters here?) cc @jkotas
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to Copilot, IL allows for creating a type with a name that contains a dot that is not treated as a namespace separator.
That does not sound correct to me. The namespace/name splitting of the full name is really just a metadata encoding optimization to avoid storing namespaces repeatedly. The ECMA-335 says: "While some programming languages introduce the concept of a namespace, the only support in the CLI
for this concept is as a metadata encoding technique. Type names are always specified by their full
name relative to the assembly in which they are defined.".
Unfortunately, ECMA-335 does not specify the namespace/name splitting algorithm. It says that you can use any dot for the split, but that makes things complicated, so it is not used in practice.
The namespace/name splitting algorithm that has been used in .NET runtime since forever is:
The last dot in the name of non-nested type is always treated as a namespace separator, except when there is a dot immediately preceding the last dot - the second to last dot is treated as a namespace separator in that case instead.
If you use the reflection emit test above to explore various cases, you should see following behavior:
Namespace..Name
-> namespace=Namespace
, name = .Name
Namespace...Name
-> namespace=Namespace.
, name = .Name
Namespace..Name.
-> namespace=Namespace..Name
, name = ``
@teo-tsirpanis Could you please add coverage for these cases?
I think we should fix the type name parser implementation to follow it to avoid inconsistencies. This algorithm is implemented by
runtime/src/libraries/Common/src/System/Reflection/Metadata/TypeNameHelpers.cs
Lines 56 to 77 in d20c35e
internal static (string typeNamespace, string name) Split(string typeName) | |
{ | |
string typeNamespace, name; | |
// Matches algorithm from ns::FindSep in src\coreclr\utilcode\namespaceutil.cpp | |
// This could result in the type name beginning with a '.' character. | |
int separator = typeName.LastIndexOf('.'); | |
if (separator <= 0) | |
{ | |
typeNamespace = ""; | |
name = typeName; | |
} | |
else | |
{ | |
if (typeName[separator - 1] == '.') | |
separator--; | |
typeNamespace = typeName.Substring(0, separator); | |
name = typeName.Substring(separator + 1); | |
} | |
return (typeNamespace, name); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameTests.cs
Show resolved
Hide resolved
src/libraries/System.Reflection.Metadata/tests/Metadata/TypeNameTests.cs
Show resolved
Hide resolved
src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Type/TypeTests.cs
Show resolved
Hide resolved
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeName.cs
Show resolved
Hide resolved
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeName.cs
Show resolved
Hide resolved
{ | ||
get | ||
{ | ||
if (IsNested) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this check be done on rootTypeName?
It does not make sense for "MyNamespace.A+B[]" to return MyNamespace
when "MyNamespace.A+B" fails with InvalidOperationException.
One test is still failing on Mono |
I took another look at the sources and have no idea on why it fails. Might try debugging it, once I manage to build Mono on Windows. |
Fixes #101774.