-
Notifications
You must be signed in to change notification settings - Fork 128
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
Port .NET Native type name parser #1472
Conversation
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.
We should try hard to avoid changing existing behavior. I didn't dig deep enough to tell how much of a difference there is between the old and new algorithms, but at least the visible differences around assembly resolution should be removed.
Also - we should take this opportunity to add more tests - the fact that none of the assembly differences caused test failures means that we're missing coverage.
Also - given the bug which started all this - we should have tests for that bug and similar cases as well.
|
||
if (a.Version != null) { | ||
Version canonicalizedVersion = a.Version.CanonicalizeVersion (); | ||
if (canonicalizedVersion.Major != ushort.MaxValue) { |
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.
Did you make any changes to these files that we should review?
@marek-safar The fact that this repo uses formatting convention not used by the .NET teams makes source sharing frustrating.
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.
I agree and this code should not land under src/linker/
but under external/<something>
where it can use any formatting convention and it's clear where it's coming from. I don't know where this code was copied from but it should also copy tests as I'm not comfortable landing hundreds of lines of a parser code without test coverage.
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.
This is originally extracted from the runtime reflection stack. The tests for that are the Type.GetType tests in the runtime repo - converting them to something workable is probably a non-trivial amount of work. If we need to service this, the fix should go through the runtime.
Given this code shipped in several major versions of a .NET runtime and the .NET Native compiler, I have pretty high confidence in it even with light (integration-level) test coverage here.
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.
Did you make any changes to these files that we should review?
Not really. Probably the most interesting change is this - how we parse the assembly version, which is a bit different from how we originally did it for .NET Native, however the code here is how we currently do it in CoreRT: https://github.com/dotnet/corert/blob/9d8bd29a71f68941aaa2c00e9c9a0eed86cafa10/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs#L26-L52
That, and the fact that the ToString
of NestedTypeName
returns DeclaringType + "/" Name
instead of DeclaringType + "+" Name
, because of how Cecil encodes things.
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.
@mateoatr please move the code under extern and undo the formating changes in the shared files
src/linker/Linker/TypeParsing.cs
Outdated
{ | ||
if (typeName is ConstructedGenericTypeName) { | ||
ConstructedGenericTypeName genericTypeName = (ConstructedGenericTypeName) typeName; | ||
return assembly.MainModule.GetType (genericTypeName.GenericType.ToString ()); |
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.
Will this lose the generic arguments?
One of the things #1387 calls out is Type.GetType("Foo[Bar]"))
should make sure both Foo and Bar are kept. It doesn't look like that would happen here.
using System.Collections.Generic; | ||
using System.Globalization; | ||
|
||
namespace System.Reflection.Runtime.Assemblies |
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.
The namespace does not match the file path (and tbh it has quite odd 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.
Should I change all the places where this namespace is used to Mono.Linker
?
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.
No - move the files that come from the runtime to the external directory like Marek suggests below (something like external\TypeParsing\System\Reflection\Runtime\Assemblies) and add a README.md to the root of TypeParsing that tells the source and commit ID that was used as the base. That way we'll have future reference.
Also undo the formatting changes on the files that come from outside so that this is easier to diff, should we have a need.
var genericTypeDef = ResolveTypeName (assembly, constructedGenericTypeName.GenericType)?.Resolve (); | ||
var genericInstanceType = new GenericInstanceType (genericTypeDef); | ||
foreach (var arg in constructedGenericTypeName.GenericArguments) { | ||
genericInstanceType.GenericArguments.Add (ResolveTypeName (assembly, arg)); |
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.
If ResolveTypeName returns null, this will create a generic instance over null. That will probably crash things.
(Also for HasElementTypeName below.)
if (foundType == null) { | ||
// Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. | ||
reflectionContext.RecordHandledPattern (); | ||
} else { | ||
reflectionContext.RecordRecognizedPattern (foundType, () => _markStep.MarkTypeVisibleToReflection (foundType, new DependencyInfo (DependencyKind.AccessedViaReflection, callingMethodDefinition), callingMethodDefinition)); | ||
methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (foundType)); | ||
var typeDef = foundType?.Resolve (); |
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.
This should be moved above the if and we should check that Resolve didn't return null. It's "easy" to create a type ref in Cecil - but there's no guarantee that it will actually resolve. If the Resolve fails here, we will likely nullref below.
@@ -1344,7 +1345,7 @@ void ProcessCreateInstanceByName (ref ReflectionPatternContext reflectionContext | |||
continue; | |||
} | |||
|
|||
MarkConstructorsOnType (ref reflectionContext, resolvedType, | |||
MarkConstructorsOnType (ref reflectionContext, resolvedType?.Resolve (), |
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.
Same here - Resolve may return null - which needs to be handled.
external/TypeParsing/System/Reflection/AssemblyNameFormatter.cs
Outdated
Show resolved
Hide resolved
* Add type parser * PR feedback * Fix mono build * Move corert code to external * Match filepaths
* Add type parser * PR feedback * Fix mono build * Move corert code to external * Match filepaths Commit migrated from dotnet/linker@ff54278
See #1387.
This adds the type name parsing logic used by CoreRT in reflection scenarios. With this we can successfully parse generic types, fixing scenarios such as #1469.
For reviewing I'd suggest paying particular attention to
ReflectionTypeNameResolver
in TypeParsing.cs; all other new files were ported from CoreRT with insignificant modifications.