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

Feature Request: Support "Missing" types on System.Reflection.MetadataLoadContext #96371

Open
wasabii opened this issue Dec 30, 2023 · 6 comments
Labels
area-System.Reflection needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration
Milestone

Comments

@wasabii
Copy link

wasabii commented Dec 30, 2023

I don't really expect this request to be accepted, because it's a bit out odd. But I wanted to document the issue for posterity.

I am working on finally rewriting IKVM.Reflection around S.R.M. And, trying to consume as much existing shared code as possible. IKVM.Reflection's API surface mostly mirrors System.Reflection, and was one of the first libraries to do assembly generation from purely managed code without using System.Reflection.

As a consequence, we have a ton of compiler code that works against the System.Reflection API. Except, we substitute it out for IKVM.Reflection in our static compiler using #ifdef. The dynamic compiler continues to use System.Reflection directly.

One option is to just rewrite IKVM.Reflection's loading and emitting API surface against S.R.M and S.R.M.E.

The other is to investigate whether other existing System.Reflection-looking APIs are at a point where we can just use them instead, and deprecate IKVM.Reflection completely. No AssemblyBuilder.Save puts the Emit side on hold for now.

But, System.Reflection.MetadataLoadContext may be a viable operation for the assembly-reading side. Except for one feature that IKVM uses: an IsMissing and ContainsMissing property on Type, Method, Field, etc.

The point of the IsMissing property on a Member is to allow a compiler to generate code that deals with partially complete assemblies. For instance, a TypeSpec in Assembly A, being built against Assembly B, where Assembly B is missing one of the target TypeRefs, can return Type with IsMissing as true. So our compiler can know this information, and use it to generate metadata against an incomplete type model. We use this to preserve Java runtime semantics in statically compiled IL.

For instance, in the example above, Assembly A might have a method that inits an object of TypeB in Assembly B, where Assembly B was generated without that type (perhaps an older version of Assembly B). Java semantics would have this method call fail at runtime, but only when it hits the portion of code where TypeB is actually required. As such, we generate the IL body for the method in Assembly A to throw the appropriate exception at that point. We emit IL to throw MethodNotFoundException. Which preserves Java semantics.

With IKVM.Reflection, we get a IKVM.Reflection.Type instead for this TypeSpec. The TypeSpec is fully resolved. But, might contain ElementTypes or generic instances, that are themselves unable to be resolved. The compiler can know this by checking ContainsMissing, and make a decision to emit a method body that throws.

With S.R.MLC, of course, any attempt to get or access the System.Type that is ultimately derived from that TypeSpec, throws TypeLoadException, leaving us unable to examine the contents of the TypeSpec and make a decision.

So, it looks like S.R.MLC is not going to be a solution for us. Which leaves us with two options: rewriting IKVM.Reflection against S.R.M and S.R.M.E, largely leaving the type model in tact; or fork S.R.MLC and introduce the notion of a Missing type. I haven't yet figured out which option I'm going to take. I'd lean towards forking, if the effort to introduce IsMissing and periodically synchronize S.R.MLC is a net benefit over maintaining our own type model.

Anyways, just thought I'd throw this up here. It's at least, as I see it, a valid use case for people attempting to build more complex IL generation routines. The type-model (S.R.MLC, System.Type, etc) is important for compilers. And, as of now, there isn't a managed-only type-model that I can find that deals with this specific circumstance: except, I'd imagine, the one in Roslyn and ours. :)

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Dec 30, 2023
@ghost
Copy link

ghost commented Dec 30, 2023

Tagging subscribers to this area: @dotnet/area-system-reflection
See info in area-owners.md if you want to be subscribed.

Issue Details

I don't really expect this request to be accepted, because it's a bit out odd. But I wanted to document the issue for posterity.

I am working on finally rewriting IKVM.Reflection around S.R.M. And, trying to consume as much existing shared code as possible. IKVM.Reflection's API surface mostly mirrors System.Reflection, and was one of the first libraries to do assembly generation from purely managed code without using System.Reflection.

As a consequence, we have a ton of compiler code that works against the System.Reflection API. Except, we substitute it out for IKVM.Reflection in our static compiler using #ifdef. The dynamic compiler continues to use System.Reflection directly.

One option is to just rewrite IKVM.Reflection's loading and emitting API surface against S.R.M and S.R.M.E.

The other is to investigate whether other existing System.Reflection-looking APIs are at a point where we can just use them instead, and deprecate IKVM.Reflection completely. No AssemblyBuilder.Save puts the Emit side on hold for now.

But, System.Reflection.MetadataLoadContext may be a viable operation for the assembly-reading side. Except for one feature that IKVM uses: an IsMissing and ContainsMissing property on Type, Method, Field, etc.

The point of the IsMissing property on a Member is to allow a compiler to generate code that deals with partially complete assemblies. For instance, a TypeSpec in Assembly A, being built against Assembly B, where Assembly B is missing one of the target TypeRefs, can return Type with IsMissing as true. So our compiler can know this information, and use it to generate metadata against an incomplete type model. We use this to preserve Java runtime semantics in statically compiled IL.

For instance, in the example above, Assembly A might have a method that inits an object of TypeB in Assembly B, where Assembly B was generated without that type (perhaps an older version of Assembly B). Java semantics would have this method call fail at runtime, but only when it hits the portion of code where TypeB is actually required. As such, we generate the IL body for the method in Assembly A to throw the appropriate exception at that point. We emit IL to throw MethodNotFoundException. Which preserves Java semantics.

With IKVM.Reflection, we get a IKVM.Reflection.Type instead for this TypeSpec. The TypeSpec is fully resolved. But, might contain ElementTypes or generic instances, that are themselves unable to be resolved. The compiler can know this by checking ContainsMissing, and make a decision to emit a method body that throws.

With S.R.MLC, of course, any attempt to get or access the System.Type that is ultimately derived from that TypeSpec, throws TypeLoadException, leaving us unable to examine the contents of the TypeSpec and make a decision.

So, it looks like S.R.MLC is not going to be a solution for us. Which leaves us with two options: rewriting IKVM.Reflection against S.R.M and S.R.M.E, largely leaving the type model in tact; or fork S.R.MLC and introduce the notion of a Missing type. I haven't yet figured out which option I'm going to take. I'd lean towards forking, if the effort to introduce IsMissing and periodically synchronize S.R.MLC is a net benefit over maintaining our own type model.

Anyways, just thought I'd throw this up here. It's at least, as I see it, a valid use case for people attempting to build more complex IL generation routines.

Author: wasabii
Assignees: -
Labels:

area-System.Reflection, untriaged

Milestone: -

@steveharter
Copy link
Member

The other is to investigate whether other existing System.Reflection-looking APIs are at a point where we can just use them instead, and deprecate IKVM.Reflection completely. No AssemblyBuilder.Save puts the Emit side on hold for now.

Note than AssemblyBuilder.Save support is in progress. This started in 8.0 and continues into 9.0.

Extending AB.Save and S.R.MLC that addresses the missing member scenarios makes sense to me if it addresses your scenarios and enables a single solution for compiler writers and others. However, that will require community research and contributions since we don't have the bandwidth at this time.

@steveharter steveharter added the needs-author-action An issue or pull request that requires more info or actions from the author. label Jan 9, 2024
@ghost
Copy link

ghost commented Jan 9, 2024

This issue has been marked needs-author-action and may be missing some important information.

@steveharter steveharter removed the untriaged New issue has not been triaged by the area owner label Jan 9, 2024
@steveharter
Copy link
Member

@wasabii any thoughts on working together to add features to S.R.MLC and leveraging the AB.Save() work being added in 9.0 (it is functional now, but still a few features being worked on)?

@wasabii
Copy link
Author

wasabii commented Jan 17, 2024

I'd be happy to. Problem is, I'm not sure whether this is even something S.R.MLC even wants to support.

@ghost ghost added needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration and removed needs-author-action An issue or pull request that requires more info or actions from the author. labels Jan 17, 2024
@IS4Code
Copy link

IS4Code commented Feb 21, 2024

I'd definitely appreciate improvements in this area, similarly in relation to #86923.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Reflection needs-further-triage Issue has been initially triaged, but needs deeper consideration or reconsideration
Projects
None yet
Development

No branches or pull requests

3 participants