-
Notifications
You must be signed in to change notification settings - Fork 127
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
PreserveDependencyAttribute on a type causes a fatal error #797
Comments
Could you share the use case why do you think it should apply to types? We very intentionally didn't allow it on types because it's ambiguous what it actually means. |
For example, a case where we need to keep XElement's internal ctor around because it's accessed by reflection from existing tools. It would seem the cleanest way to do this is to put a
How so? |
As another example, we have several places where Visual Studio depends on internal/private methods existing on a type. How can I use [PreserveDependency(...)] to keep those alive? I could do so by putting such an attribute on the type itself, such that as long as the type is around, the methods won't be trimmed. But today it seems my only option is the ILLinkTrim.xml file. |
Another example: if PreserveDependency could be placed on types, then I'd have a workaround for #801. |
That does not make much sense to me. There is no guarantee the XElement type will be kept. What reflection pattern does the tool use, perhaps we could fix that instead? What about making the ctor public when it's used so often we need to keep it all the time?
Just two examples, imagine the type which has PreserveDependency at type level is used only as cast. Should that bring the dependency in? What about case where the type is used at declaration level only, something like If we really need something like this I'd prefer to use a different attribute for this. For example, use reverse order and specify caller dependency at the constructor. |
If XElement isn't kept, then there's no need to keep the ability to construct it.
I don't think we should be deciding on the public shape of API (especially API like this that's been around for a long time) based on the needs of optimization tooling. If I'm understanding your proposal, it would also entail changing the consuming code; I'm not even sure what/where that code is.
Sorry, I'm not seeing the ambiguity. The attribute shouldn't affect whether the type that's attributed is kept. It only says "if you keep the attributed type, then also keep this dependency". If the type is kept because of the cast, then so would its dependency. |
I was speaking with @marek-safar the other day and he asked me to share my thoughts on this specific request as well as my thoughts on an attribute based api for controlling stripping in general. I've been thinking about this in the background for quite some time. Unity end-users, developers at Unity , and us subset of developers at Unity who are responsible for mono class libraries all have different goals, constraints, use cases, and as a result we all want slightly different things. First let me recap the mechanisms we have today and the pros/cons of them. link.xmlThis is the xml file format that lets you explicitly preserve anything. This is a great backdoor to fix up code you do not own. It's not such a great api for managing the stripping of code you do have control over. It can easily break from renaming, getting full signatures specified correctly can be a huge pain, and there is no way to conditionally define a preservation. Unity/mono used to depend exclusively on this mechanism for preserving things in the class libraries that were needed by the runtime, or reflection in the class libraries. This was imprecise and results in over preservation. Both unity & mono have been trying to move away from this mechanism. My experience with Unity end-users is that they rarely care enough to figure out exactly what is being stripped away that is needed and then adding just that to the link.xml file. What they do instead is just start preserving entire assemblies via link.xml until the problem goes away. [Preserve]This is an attribute unique to Unity. We've had it around for a long time. You can put it on anything and that thing will be preserved no matter what. This mechanism is easier to use than link.xml. You don't have to figure out the full signature of anything, but it still suffers from the same problem as link.xml in that it essentially causes the type/member to be a root. Additionally, if the [PreserveDependency]This was added by @marek-safar a year or so ago. It let's a method precisely say that it depends on something else. This is great for 1 off things where a dependency cannot be detected by the linker. Such as calls into native code that then callback into managed code. Or a reflection usage that cannot be detected. However, this attribute suffers from the same figure-out-the-signature problem as link.xml. It can easily break from renaming. It's not great if a method has many dependencies. It simply doesn't work for reflection patterns where you do something like find all types that implement an interface. And it's not a practical tool for poking around trying to guess what was incorrectly stripped. Remember, in most scenarios to date, the developer doesn't know what was incorrectly stripped. Add a usage hackI've seen variations of this numerous times. It may even be more commonly used than link.xml and [Preserve] for preserving individual methods that are being stripped when they are needed.
I see the appeal of this. It's essentially the same behavior as link.xml without the headaches. The conclusion I've reachedAll of this has led me to believe that different types of users/code bases will want different types of attributes for controlling the linker.
What about allowing [PreserveDependcy] on a TypeNow onto the request in this issue.
Based on these comments my feeling is that these are dependencies of external tools / code. Not of I will say that if you do absolutely need a behavior like you are requesting, I'd lean away from using What could a more complete attribute api look like?This post is getting pretty long. I'll go into thoughts I have on this in a follow on post. |
Over the last year or so I've started to accumulate additional attribute ideas that would solve a particular use case that someone would come to me with. Sometimes the use case would come from a developer at Unity and other times it would come from one of our end users. We have also begun linking the assemblies that are in our IL2CPP test suite. These assemblies are a great accumulation of coding patterns. Sometimes these tests can be contrived examples, but non the less we started adding attributes that our UnityLinker supports that would allow us to annotate these tests in a sane way. My thinking was that some day we may end up with a nice set of attributes for annotating code stripping that we could then polish up and expose to our users. I'll go into details about the attributes we internally support as well as some other ideas I've only jotted down to give everyone an idea of what an ideal set of attributes could look like that would satisfy the many different types of users that are out there. [RequiredMember]When the UnityLinker does support this attribute, however, it is not officially supported in Unity. [RequiredInterface(Type)]Allowed on a Type. When the type is marked, so will all InterfaceImplementations of the specified type. Generally an attribute like this shouldn't be necessary. However, there is at least one api doesn't play nice with the linkers interface stripping behavior.
UnityLinker does support this attribute, however, it is not officially supported in Unity. [RequireAttributeUsages]Can be placed on classes, although it will only have an effect if placed on an attribute class. This attribute tells the linker to mark all CustomAttributes of this type no matter what. The use case for this one is commonly when code external to the assemblies being linked are going to inspect the assemblies and look for certain attributes. In this scenario, combine with the linkers UnityLinker does support this attribute, however, it is not officially supported in Unity. [AlwaysLinkAssembly]This is an officially supported attribute in Unity. See documentation here https://docs.unity3d.com/Manual/ManagedCodeStripping.html This is likely Unity specific, but I thought I'd mention it in case someone encounters a need that feels similar. Now onto the attributes that I've merely jotted down as ideas but have never implemented support for.Disclaimer. These are all just ideas. Some of the names may not be great 😄 [InstanceCreated]This attribute is similar to what @stephentoub is requesting in this issue.
I believe my original motivation was related to the uninstantiated type work. I don't know if this attribute should cause the default ctor to be marked or not. I think it could help with the "most people don't realize the TypeDefinition is separate from the default ctor issue". [PreserveMembersIfTypeMarked]Once a TypeDefinition is marked, mark all of it's members. This one is catered toward end users. Maybe they have a heavy reflection that applies to a class. It could also act as a wide'ish net to start casting in order to find a stripping problem. Recall that a lot of developers quickly resort to preserving the entire assembly via link.xml and then may never come back to optimize. This attribute is at least a little more granular. I made a note about optionally having some member level granularity exposed. I.e. "Preserve all fields if the type is marked" or "Preserve all private methods if the type is marked". That might be overkill. I don't have an specific example where this was necessary. [RequireImplementors]Can be added to an interface type. All classes, structs, and interfaces implementing this interface will be marked if the interface type is marked. A request for something like this has come up numerous times as a way to handle coding patterns where all types implementing some interface are collected and something is done with them. Maybe create instance via reflection and then call some interface method. [RequireNestedType]Can be placed on a type to indicate that when the type is marked the specified nested type should also be marked. I never had a request where this was needed. I came up with the idea for the sake of completeness when I implemented support for RequireSubclassesIf the type is marked, all derived types will be marked as well. Useful during the same pattern as [PreserveMembers]All members of the type are preserved no matter what. An extension of [CreatesInstance(Type)]Can be placed on a method. Signals that a method creates an instance of the specified type. Scenario where this might be desired is an icall that will create an instance of a type from native code. I think I also jotted this down when I was adding the instantiated type tracking to the linker. So I'm sure another motivation for the idea was as a way to by pass the optimizations the linker makes when it determines a type is never instantiated. [SuppressReflectionWarning]Could be placed on a method to tell the linker to not issue a warning about a reflection usage it could not figure out. I think it would be interesting if we ever got to a point where the linker would issue a warning on all undetectable reflection and calls into native code that did not have some sort of attribute on them telling the linker "I know you can't figure out something here, it's OK, I've added the proper annotations to ensure the dependencies of this method will be marked". We could then maybe get to a point where we could "know" (assuming you can trust the suppression's) that all dependencies of a linked program were marked. There's a brain dump of the ideas I've accumulated. I've never sat down and thought about all the different scenarios developers may encounter. I've just collected ideas as I've encountered issues. I think it would be great if someone took some time to come up with a thought out set of attribute API's that would satisfy the entire .net ecosystem. I've attached the project where I was collecting ideas and some of our linker tests for attributes we do support. I tried to summarize as much as I could here, but there might be a few more insights to find in these. StrippingAttributePrototyping.zip |
To add to this, here's some recent analysis I did on Blazor's usage scenarios: dotnet/aspnetcore#17022 TLDR - today we run the linker on the BCL only, we want to run it on all Blazor's assemblies as well, and possibly user-code (opt-in) in the future. The things that break Blazor today are mostly the removal of property accessors and constructors, which are not big contributors to size. Having the ability to configure the granularity of marking via command line switches or attributes would really help us solve this in a maintainable way. |
For the sake of record keeping, another idea came up in #854 (comment) [RequireMembersWithAttribute]You would put this on the attribute type and the linker would mark all members that have this attribute. We could add some sort of optional enum for controlling what should trigger the marking. i.e. Always or ParentMarked. This would be useful for things like |
PreserveDependencyAttribute has been superseded with DynamicDependencyAttribute so closing this issue. |
The PreserveDependencyAttribute defined in mono currently doesn't allow it to be specified on types, but it should. When I add that allowance in corefx and try to use it on a type, however, the linker blows up with a null ref:
cc: @marek-safar
The text was updated successfully, but these errors were encountered: