-
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
Consider PreserveDependencyAttribute to help linker #30902
Comments
I agree that this is a good idea. The PreserveDependencyAttribute as it exists today is not compatible with per-assembly trimming mode that we use to remove unnecessary code from CoreLib and CoreFX today. We would need to do something about it. Options I can think of:
|
Yes, I just had a discussion with Marek about that. I think there's another options as well:
|
The PreserveDependency is specified on the consuming side, but the trimming needs to be suppressed on the producing side. These two often live in different assemblies. How would the linker get to know about the consuming assembly when trimming the producing assembly? |
The two most common cases I've seen are:
Both of those would be unaffected. There is the third case you mention, where there's a cross-assembly dependency to something non-public. I've seen very few cases of that, but they do exist. For example, for better or worse System.Net.HttpListener today accesses Cookie.ToServerString, which is internal, via reflection. If we put PreserveDependency in HttpListener, it would have the problem you mention. We could instead put PreserveDependency on the Cookie's ctor, such that ToServerString only need be kept if it's possible it could be called. That's certainly not ideal, as then ToServerString is kept around even if HttpListener isn't used, but at least thus far I haven't seen this to be a widespread issue, since we actively discourage private reflection across assembly boundaries, for others and for ourselves. This case could also be addressed by just adding a parameterless ctor to [PreserveDependency] and saying that anything attributed with it will be kept. I think this is basically your option 4, albeit your option 4 could be more flexible. The other mentioned options also have some downsides:
|
@jkotas the proposed command line option would be global for the linker run, basically not removing this attribute anywhere. |
In light of dotnet/linker#805 we should look for a different name: Some proposed names from the issue:
|
I like the |
This is more like |
Is this a customer scenario? It feels like the use cases where we need to preserve something because native code is essentially going to "reflect" on this are more our internal/Unity use cases and not something mainstream customers would hit. (I want to make sure we optimize the name for customer scenarios and not our internal use cases.) It feels that the callbacks from native code still fit the "dynamic" name because they're all essentially just invoked through a different kind of reflection (get looked up using string names, called in a funny way, etc.). But I don't have a very strong preference for this attribute in particular. |
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Method |
AttributeTargets.Constructor |
AttributeTargets.Field,
AllowMultiple = true,
Inherited = false)]
public sealed class DynamicDependencyAttribute : Attribute
{
public DynamicDependencyAttribute(string memberSignature);
public DynamicDependencyAttribute(string memberSignature, string typeName, string assemblyName);
public DynamicDependencyAttribute(DynamicallyAccessedMemberKinds memberKinds, string typeName, string assemblyName);
public DynamicDependencyAttribute(string memberSignature, Type type);
public DynamicDependencyAttribute(DynamicallyAccessedMemberKinds memberKinds, Type type);
public DynamicallyAccessedMemberKinds? MemberKinds { get; }
public string? MemberSignature { get; }
public string? TypeName { get; }
public string? AssemblyName { get; }
public string? Condition { get; set; }
}
} |
DynamicallyAccessedMembersAttribute DynamicDependencyAttribute UnconditionalSuppressMessageAttribute Fix dotnet#33861, dotnet#35339, dotnet#30902
Closed in #35387. |
We currently use ILLinkTrim.xml files to express APIs, whether public or non-public, that should not be trimmed away by the linker, whether during the first trimming phase when we build the assemblies or the second optional trimming phase when an app is deployed with trimming.
However, these declarations are causing us to keep around a lot more state than is necessary, for two reasons:
For example, in System.Data.Common, we have an ILLinkTrim.xml file that says the DataTableTypeConverter's .ctor() should be preserved. That's because the DataView.Table property is annotated with a TypeConverterAttribute:
https://github.com/dotnet/corefx/blob/19b304f7815894b13cb61e87e1c9eac49a474c7e/src/System.Data.Common/src/System/Data/DataView.cs#L473
and the type converter infrastructure needs access to that ctor to instantiate it via reflection. However, because that .ctor is mentioned in the .xml file, it'll never be trimmed away, even if DataView.Table itself isn't used. And DataTableTypeConverter's ctor references DataTable, which in turn references a whole bunch of stuff.
The same goes for other assemblies, e.g. System.Text.Json, System.Private.DataContractSerialization, etc.
We should consider instead exposing and using the PreserveDependencyAttribute that the linker already has some support for (though coreclr and corefx are currently using too old a version), e.g.
This, or something like it, has the ability to specify not just that something is needed, but why that thing is needed, in a way where if the reason for it needing to be there gets trimmed away, so too does the need. It also has the benefit that the dependency is expressed in the code rather than in a separate asset.
cc: @sbomer, @jkotas, @marek-safar
The text was updated successfully, but these errors were encountered: