From 99166104f513bf0ca2ec7fa4f43846d247d734ad Mon Sep 17 00:00:00 2001 From: sakno Date: Sun, 28 Jan 2024 21:59:46 +0200 Subject: [PATCH] Initial work for IL trimming --- .../DotNext.Metaprogramming.csproj | 4 +-- .../Linq/Expressions/ExpressionBuilder.cs | 27 ++++++++++++++----- .../Linq/Expressions/ForEachExpression.cs | 2 ++ .../Linq/Expressions/ItemIndexExpression.cs | 3 +++ .../Linq/Expressions/MemberBindings.cs | 3 ++- .../AsyncStateMachineBuilder.cs | 11 ++++---- .../CompilerServices/ClosureAnalyzer.cs | 1 + .../InterpolatedStringTemplateBuilder.cs | 19 ++++++++++--- 8 files changed, 50 insertions(+), 20 deletions(-) diff --git a/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj b/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj index 9cbc9292f7..e62aa45b4e 100644 --- a/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj +++ b/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj @@ -6,9 +6,9 @@ latest enable true - false + true nullablePublicOnly - 5.0.1 + 5.0.2 .NET Foundation .NEXT Family of Libraries diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs b/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs index 9ad3e4eed3..f479ab686c 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs @@ -251,6 +251,7 @@ public static BinaryExpression NotEqual(this Expression left, Expression right) /// /// The operand. /// check operation. + [RequiresUnreferencedCode("Dynamic access to properties of .")] public static Expression IsNull(this Expression operand) { // handle nullable value type @@ -277,6 +278,7 @@ public static Expression IsNull(this Expression operand) /// /// The operand. /// check operation. + [RequiresUnreferencedCode("Dynamic access to properties of .")] public static Expression IsNotNull(this Expression operand) { // handle nullable value type @@ -723,6 +725,7 @@ public static MethodCallExpression Call(this Expression instance, MethodInfo met /// The name of the method to be called. /// The method arguments. /// The method call expression. + [RequiresUnreferencedCode("Dynamic access to the method identified by parameter.")] public static MethodCallExpression Call(this Expression instance, string methodName, params Expression[] arguments) => instance.Call(instance.Type, methodName, arguments); @@ -739,7 +742,7 @@ public static MethodCallExpression Call(this Expression instance, string methodN /// The name of the method in the interface or base class to be called. /// The method arguments. /// The method call expression. - public static MethodCallExpression Call(this Expression instance, Type interfaceType, string methodName, params Expression[] arguments) + public static MethodCallExpression Call(this Expression instance, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type interfaceType, string methodName, params Expression[] arguments) { if (!interfaceType.IsAssignableFrom(instance.Type)) throw new ArgumentException(ExceptionMessages.InterfaceNotImplemented(instance.Type, interfaceType)); @@ -756,7 +759,7 @@ public static MethodCallExpression Call(this Expression instance, Type interface /// The name of the static method. /// The arguments to be passed into static method. /// An expression representing static method call. - public static MethodCallExpression CallStatic(this Type type, string methodName, params Expression[] arguments) + public static MethodCallExpression CallStatic([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type, string methodName, params Expression[] arguments) { return type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly, null, Array.ConvertAll(arguments, GetType), null) is { } method ? Expression.Call(method, arguments) @@ -799,7 +802,7 @@ public static IndexExpression Property(this Expression instance, PropertyInfo pr /// The interface or base class declaring property. /// The name of the instance property. /// Property access expression. - public static MemberExpression Property(this Expression instance, Type interfaceType, string propertyName) + public static MemberExpression Property(this Expression instance, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type interfaceType, string propertyName) { return interfaceType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance) is { } property ? Property(instance, property) @@ -818,7 +821,7 @@ public static MemberExpression Property(this Expression instance, Type interface /// The first index. /// The rest of the indexer arguments. /// Property access expression. - public static IndexExpression Property(this Expression instance, Type interfaceType, string propertyName, Expression index0, params Expression[] indicies) + public static IndexExpression Property(this Expression instance, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type interfaceType, string propertyName, Expression index0, params Expression[] indicies) { return interfaceType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance) is { } property ? Property(instance, property, index0, indicies) @@ -834,6 +837,7 @@ public static IndexExpression Property(this Expression instance, Type interfaceT /// this argument. /// The name of the instance property. /// Property access expression. + [RequiresUnreferencedCode("Dynamic access to the property identified by parameter.")] public static MemberExpression Property(this Expression instance, string propertyName) => Expression.Property(instance, propertyName); @@ -848,6 +852,7 @@ public static MemberExpression Property(this Expression instance, string propert /// The first index. /// The rest of the indexer arguments. /// Property access expression. + [RequiresUnreferencedCode("Dynamic access to the indexer identified by parameter.")] public static IndexExpression Property(this Expression instance, string propertyName, Expression index0, params Expression[] indicies) => Expression.Property(instance, propertyName, [index0, .. indicies]); @@ -872,6 +877,7 @@ public static MemberExpression Field(this Expression instance, FieldInfo field) /// this argument. /// The name of the instance field. /// Field access expression. + [RequiresUnreferencedCode("Dynamic access to the field identified by parameter.")] public static MemberExpression Field(this Expression instance, string fieldName) => Expression.Field(instance, fieldName); @@ -937,10 +943,12 @@ public static UnaryExpression ArrayLength(this Expression array) /// /// The expression representing collection. /// The expression providing access to the appropriate property indicating the number of items in the collection. + [RequiresUnreferencedCode("Dynamic access to implemented interfaces and public properties of .")] public static MemberExpression Count(this Expression collection) { if (collection.Type == typeof(string) || collection.Type == typeof(StringBuilder)) return Expression.Property(collection, nameof(string.Length)); + var interfaceType = collection.Type.GetImplementedCollection() ?? throw new ArgumentException(ExceptionMessages.CollectionImplementationExpected); return Expression.Property(collection, interfaceType, nameof(Count)); } @@ -950,6 +958,7 @@ public static MemberExpression Count(this Expression collection) /// /// The object to be converted into string. /// The expression representing ToString() method call. + [RequiresUnreferencedCode("Dynamic access to public methods of .")] public static MethodCallExpression AsString(this Expression obj) => Call(obj, nameof(ToString)); /// @@ -1136,12 +1145,12 @@ public static ConditionalExpression Condition(this Expression test, Exp /// The type to be instantiated. /// The list of arguments to be passed into constructor. /// Instantiation expression. - public static NewExpression New(this Type type, params Expression[] args) + public static NewExpression New([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] this Type type, params Expression[] args) { if (args.LongLength is 0L) return Expression.New(type); - return type.GetConstructor(Array.ConvertAll(args, static arg => arg.Type)) is { } ctor + return type.GetConstructor(Array.ConvertAll(args, GetType)) is { } ctor ? Expression.New(ctor, args) : throw new MissingMethodException(type.FullName, ConstructorInfo.ConstructorName); } @@ -1155,6 +1164,7 @@ public static NewExpression New(this Type type, params Expression[] args) /// An expression representing object construction. /// A collection of members to initialize. /// Initialization expression. + [RequiresUnreferencedCode("Dynamic access to public properties of .")] public static MemberInitExpression Init(this NewExpression expression, MemberBindings bindings) => Expression.MemberInit(expression, bindings.Bind(expression.Type)); @@ -1203,7 +1213,7 @@ internal static Expression AddEpilogue(this Expression expression, bool inferTyp /// The expression representing the type to be instantiated. /// The list of arguments to be passed into constructor. /// Instantiation expression. - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(Activator))] + [RequiresUnreferencedCode("Dynamic access to public constructors of a type represented by .")] public static MethodCallExpression New(this Expression type, params Expression[] args) { var activate = typeof(Activator).GetMethod(nameof(Activator.CreateInstance), [typeof(Type), typeof(object[])]); @@ -1217,6 +1227,7 @@ public static MethodCallExpression New(this Expression type, params Expression[] /// The collection to iterate through. /// A delegate that is used to construct the body of the loop. /// The constructed loop. + [RequiresUnreferencedCode("Dynamic access to GetEnumerator method and IEnumerable interfaces.")] public static ForEachExpression ForEach(this Expression collection, ForEachExpression.Statement body) => ForEachExpression.Create(collection, body); @@ -1453,6 +1464,7 @@ public static Expression AsOptional(this Expression expression) /// /// The compound expression. /// The expression of type . + [RequiresUnreferencedCode("Dynamic access to DotNext.Result<> data type")] public static Expression AsResult(this Expression expression) { var exception = Expression.Parameter(typeof(Exception)); @@ -1465,6 +1477,7 @@ public static Expression AsResult(this Expression expression) Expression.Catch(exception, Expression.New(fallbackCtor, exception))); } + [RequiresUnreferencedCode("Dynamic access to indexer of .")] internal static IndexExpression MakeIndex(Expression target, Expression[] args) { // handle case for array diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/ForEachExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/ForEachExpression.cs index 373c0bc410..810a761a2e 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/ForEachExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/ForEachExpression.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; @@ -13,6 +14,7 @@ namespace DotNext.Linq.Expressions; /// Represents iteration over collection elements as expression. /// /// foreach Statement +[RequiresUnreferencedCode("Dynamic access to GetEnumerator method and IEnumerable interfaces.")] public sealed class ForEachExpression : CustomExpression, ILoopLabels { private const string EnumeratorVarName = "enumerator"; diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/ItemIndexExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/ItemIndexExpression.cs index 1369ac13c3..af52a8a532 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/ItemIndexExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/ItemIndexExpression.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; using Debug = System.Diagnostics.Debug; @@ -64,6 +65,8 @@ public ItemIndexExpression(Expression value, bool fromEnd = false) /// public override Type Type => typeof(Index); + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(Index))] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Dependency of Index is declared explicitly")] internal static Expression GetOffset(Expression index, Expression count) => index is ItemIndexExpression { IsFromEnd: false } itemIndex ? itemIndex.Value : Call(index, nameof(Index.GetOffset), null, count); diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/MemberBindings.cs b/src/DotNext.Metaprogramming/Linq/Expressions/MemberBindings.cs index 9b22273ef8..f8b0bb4458 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/MemberBindings.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/MemberBindings.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; @@ -21,7 +22,7 @@ public MemberBindings() /// /// The target type with the declared members. /// A list of bindings. - public IReadOnlyList Bind(Type target) + public IReadOnlyList Bind([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target) { const MemberTypes memberTypes = MemberTypes.Field | MemberTypes.Property; const BindingFlags memberFlags = BindingFlags.Public | BindingFlags.Instance; diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs index b7aa4ff015..f54f19522b 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs @@ -21,6 +21,7 @@ namespace DotNext.Runtime.CompilerServices; /// 2. Construct state holder type /// 3. Replace all local variables with fields from state holder type. /// +[RequiresUnreferencedCode("Dynamic access to DotNext.Metaprogramming internal types, Task and ValueTask.")] internal sealed class AsyncStateMachineBuilder : ExpressionVisitor, IDisposable { private static readonly UserDataSlot ParameterPositionSlot = new(); @@ -57,7 +58,7 @@ internal AsyncStateMachineBuilder(Type taskType, IReadOnlyList : ExpressionVisitor, IDisposable +[RequiresUnreferencedCode("Dynamic access to Transition and IAsyncStateMachine internal types.")] +internal sealed class AsyncStateMachineBuilder<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TDelegate> : ExpressionVisitor, IDisposable where TDelegate : Delegate { private readonly AsyncStateMachineBuilder methodBuilder; @@ -509,10 +511,7 @@ internal StateMachineBuilder(Type returnType, bool usePooling) this.usePooling = usePooling; } - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(AsyncStateMachine<>))] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(AsyncStateMachine<,>))] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PoolingAsyncStateMachine<>))] - [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PoolingAsyncStateMachine<,>))] + [RequiresUnreferencedCode("Dynamic access to AsyncStateMachine and PoolingAsyncStateMachine internal types.")] internal MemberExpression Build(Type stateType) { Type stateMachineType; diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/ClosureAnalyzer.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/ClosureAnalyzer.cs index a064b4c281..ab9ef42391 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/ClosureAnalyzer.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/ClosureAnalyzer.cs @@ -4,6 +4,7 @@ namespace DotNext.Runtime.CompilerServices; +[RequiresUnreferencedCode("Dynamic access to StrongBox type.")] internal sealed class ClosureAnalyzer : ExpressionVisitor { private static readonly UserDataSlot ClosureVariableSlot = new(); diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/InterpolatedStringTemplateBuilder.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/InterpolatedStringTemplateBuilder.cs index 809a71ee78..3c8516e8ec 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/InterpolatedStringTemplateBuilder.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/InterpolatedStringTemplateBuilder.cs @@ -1,5 +1,6 @@ using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -40,6 +41,8 @@ internal Segment(Type argumentType, string? format, int alignment) this.argumentType = argumentType; } + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(BufferWriterSlimInterpolatedStringHandler))] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "DynamicDependencyAttribute is applied")] internal void WriteStatement(IList statements, ParameterExpression provider, ParameterExpression handler, out ParameterExpression? inputVar) { Debug.Assert(provider.Type == typeof(IFormatProvider)); @@ -145,11 +148,19 @@ public void AppendFormatted(Type type, string? format = null) /// the renderer of the interpolated string. /// /// The lambda expression that encapsulates the rendering logic. + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PreallocatedCharBuffer))] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(BufferWriterSlim))] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(BufferWriterSlimInterpolatedStringHandler))] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "DynamicDependencyAttribute is applied")] public readonly LambdaExpression Build() { var preallocatedBufferLocal = Expression.Variable(typeof(PreallocatedCharBuffer), "buffer"); - var writerLocal = Expression.Variable(typeof(BufferWriterSlim), "writer"); - var handlerLocal = Expression.Variable(typeof(BufferWriterSlimInterpolatedStringHandler), "handler"); + + var bufferWriterSlimType = typeof(BufferWriterSlim); + var writerLocal = Expression.Variable(bufferWriterSlimType, "writer"); + + var stringHandlerType = typeof(BufferWriterSlimInterpolatedStringHandler); + var handlerLocal = Expression.Variable(stringHandlerType, "handler"); var providerParameter = Expression.Parameter(typeof(IFormatProvider), "provider"); var allocatorParameter = Expression.Parameter(typeof(MemoryAllocator), "allocator"); @@ -161,7 +172,7 @@ public readonly LambdaExpression Build() var statements = new List(); // instantiate buffer writer - var ctor = writerLocal.Type.GetConstructor([typeof(Span), allocatorParameter.Type]); + var ctor = bufferWriterSlimType.GetConstructor([typeof(Span), allocatorParameter.Type]); Debug.Assert(ctor is not null); Expression expr = Expression.New( ctor, @@ -170,7 +181,7 @@ public readonly LambdaExpression Build() statements.Add(Expression.Assign(writerLocal, expr)); // instantiate handler - ctor = handlerLocal.Type.GetConstructor([typeof(int), typeof(int), writerLocal.Type.MakeByRefType(), providerParameter.Type]); + ctor = stringHandlerType.GetConstructor([typeof(int), typeof(int), writerLocal.Type.MakeByRefType(), providerParameter.Type]); Debug.Assert(ctor is not null); expr = Expression.New( ctor,