Skip to content
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

Merged
merged 6 commits into from
Sep 16, 2020
Merged

Conversation

mateoatr
Copy link
Contributor

@mateoatr mateoatr commented Sep 5, 2020

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.

Copy link
Member

@vitek-karas vitek-karas left a 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) {
Copy link
Member

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.

Copy link
Contributor

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.

Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MichalStrehovsky

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.

Copy link
Contributor

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

{
if (typeName is ConstructedGenericTypeName) {
ConstructedGenericTypeName genericTypeName = (ConstructedGenericTypeName) typeName;
return assembly.MainModule.GetType (genericTypeName.GenericType.ToString ());
Copy link
Member

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
Copy link
Contributor

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)

Copy link
Contributor Author

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?

Copy link
Member

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));
Copy link
Member

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 ();
Copy link
Member

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 (),
Copy link
Member

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.

@marek-safar marek-safar merged commit ff54278 into dotnet:master Sep 16, 2020
mateoatr added a commit to mateoatr/linker that referenced this pull request Nov 13, 2020
* Add type parser

* PR feedback

* Fix mono build

* Move corert code to external

* Match filepaths
agocke pushed a commit to dotnet/runtime that referenced this pull request Nov 16, 2022
* Add type parser

* PR feedback

* Fix mono build

* Move corert code to external

* Match filepaths

Commit migrated from dotnet/linker@ff54278
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants