Skip to content

Consider PreserveDependencyAttribute to help linker #30902

@stephentoub

Description

@stephentoub

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:

  1. The presence of such an .xml file is forcing the binary to be preserved, even if nothing from it ends up being used.
  2. It doesn't provide the ability to say "only keep XYZ if ABC is used".

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.

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, AllowMultiple = true, Inherited = false)]
    internal sealed class PreserveDependencyAttribute : Attribute
    {
        public PreserveDependencyAttribute(string? memberSignature)
        {
            MemberSignature = memberSignature;
        }

        public PreserveDependencyAttribute(string? memberSignature, string typeName)
        {
            MemberSignature = memberSignature;
            TypeName = typeName;
        }

        public PreserveDependencyAttribute(string? memberSignature, string typeName, string assemblyName)
        {
            MemberSignature = memberSignature;
            TypeName = typeName;
            AssemblyName = assemblyName;
        }

        public string? MemberSignature { get; set; }
        public string? TypeName { get; set; }
        public string? AssemblyName { get; set; }

        public string? Condition { get; set; }
    }
}

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

Metadata

Metadata

Assignees

Labels

api-approvedAPI was approved in API review, it can be implementedarea-Metalinkable-frameworkIssues associated with delivering a linker friendly framework

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions