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

Add IL trimming support for DotNext.Metaprogramming #219

Draft
wants to merge 5 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<IsTrimmable>false</IsTrimmable>
<IsTrimmable>true</IsTrimmable>
<Features>nullablePublicOnly</Features>
<VersionPrefix>5.0.1</VersionPrefix>
<VersionPrefix>5.0.2</VersionPrefix>
<VersionSuffix></VersionSuffix>
<Authors>.NET Foundation</Authors>
<Product>.NEXT Family of Libraries</Product>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;

Expand Down Expand Up @@ -138,11 +139,11 @@ public override Expression Reduce()
if (indexer is null)
result = ArrayAccess(temp ?? Collection, Index);
else if (count is null)
result = MakeIndex(temp ?? Collection, indexer, new[] { Index.Reduce() });
result = MakeIndex(temp ?? Collection, indexer, [Index.Reduce()]);
else
result = MakeIndex(temp ?? Collection, indexer, new[] { MakeIndex(temp ?? Collection, count, Index) });
result = MakeIndex(temp ?? Collection, indexer, [MakeIndex(temp ?? Collection, count, Index)]);

return temp is null ? result : Block(Type, new[] { temp }, Assign(temp, Collection), result);
return temp is null ? result : Block(Type, [temp], Assign(temp, Collection), result);
}

/// <summary>
Expand All @@ -156,4 +157,4 @@ protected override CollectionAccessExpression VisitChildren(ExpressionVisitor vi
var collection = visitor.Visit(Collection);
return ReferenceEquals(index, Index) && ReferenceEquals(collection, Collection) ? this : new(collection, index);
}
}
}
33 changes: 23 additions & 10 deletions src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ public static BinaryExpression NotEqual(this Expression left, Expression right)
/// </remarks>
/// <param name="operand">The operand.</param>
/// <returns><see langword="null"/> check operation.</returns>
[RequiresUnreferencedCode("Dynamic access to properties of <operand>.")]
public static Expression IsNull(this Expression operand)
{
// handle nullable value type
Expand All @@ -277,6 +278,7 @@ public static Expression IsNull(this Expression operand)
/// </remarks>
/// <param name="operand">The operand.</param>
/// <returns><see langword="null"/> check operation.</returns>
[RequiresUnreferencedCode("Dynamic access to properties of <operand>.")]
public static Expression IsNotNull(this Expression operand)
{
// handle nullable value type
Expand Down Expand Up @@ -723,6 +725,7 @@ public static MethodCallExpression Call(this Expression instance, MethodInfo met
/// <param name="methodName">The name of the method to be called.</param>
/// <param name="arguments">The method arguments.</param>
/// <returns>The method call expression.</returns>
[RequiresUnreferencedCode("Dynamic access to the method identified by <methodName> parameter.")]
public static MethodCallExpression Call(this Expression instance, string methodName, params Expression[] arguments)
=> instance.Call(instance.Type, methodName, arguments);

Expand All @@ -739,7 +742,7 @@ public static MethodCallExpression Call(this Expression instance, string methodN
/// <param name="methodName">The name of the method in the interface or base class to be called.</param>
/// <param name="arguments">The method arguments.</param>
/// <returns>The method call expression.</returns>
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));
Expand All @@ -756,7 +759,7 @@ public static MethodCallExpression Call(this Expression instance, Type interface
/// <param name="methodName">The name of the static method.</param>
/// <param name="arguments">The arguments to be passed into static method.</param>
/// <returns>An expression representing static method call.</returns>
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)
Expand Down Expand Up @@ -799,7 +802,7 @@ public static IndexExpression Property(this Expression instance, PropertyInfo pr
/// <param name="interfaceType">The interface or base class declaring property.</param>
/// <param name="propertyName">The name of the instance property.</param>
/// <returns>Property access expression.</returns>
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)
Expand All @@ -818,7 +821,7 @@ public static MemberExpression Property(this Expression instance, Type interface
/// <param name="index0">The first index.</param>
/// <param name="indicies">The rest of the indexer arguments.</param>
/// <returns>Property access expression.</returns>
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)
Expand All @@ -834,6 +837,7 @@ public static IndexExpression Property(this Expression instance, Type interfaceT
/// <param name="instance"><c>this</c> argument.</param>
/// <param name="propertyName">The name of the instance property.</param>
/// <returns>Property access expression.</returns>
[RequiresUnreferencedCode("Dynamic access to the property identified by <propertyName> parameter.")]
public static MemberExpression Property(this Expression instance, string propertyName)
=> Expression.Property(instance, propertyName);

Expand All @@ -848,6 +852,7 @@ public static MemberExpression Property(this Expression instance, string propert
/// <param name="index0">The first index.</param>
/// <param name="indicies">The rest of the indexer arguments.</param>
/// <returns>Property access expression.</returns>
[RequiresUnreferencedCode("Dynamic access to the indexer identified by <propertyName> parameter.")]
public static IndexExpression Property(this Expression instance, string propertyName, Expression index0, params Expression[] indicies)
=> Expression.Property(instance, propertyName, [index0, .. indicies]);

Expand All @@ -872,6 +877,7 @@ public static MemberExpression Field(this Expression instance, FieldInfo field)
/// <param name="instance"><c>this</c> argument.</param>
/// <param name="fieldName">The name of the instance field.</param>
/// <returns>Field access expression.</returns>
[RequiresUnreferencedCode("Dynamic access to the field identified by <fieldName> parameter.")]
public static MemberExpression Field(this Expression instance, string fieldName)
=> Expression.Field(instance, fieldName);

Expand Down Expand Up @@ -937,10 +943,12 @@ public static UnaryExpression ArrayLength(this Expression array)
/// </remarks>
/// <param name="collection">The expression representing collection.</param>
/// <returns>The expression providing access to the appropriate property indicating the number of items in the collection.</returns>
[RequiresUnreferencedCode("Dynamic access to implemented interfaces and public properties of <collection>.")]
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));
}
Expand All @@ -950,6 +958,7 @@ public static MemberExpression Count(this Expression collection)
/// </summary>
/// <param name="obj">The object to be converted into string.</param>
/// <returns>The expression representing <c>ToString()</c> method call.</returns>
[RequiresUnreferencedCode("Dynamic access to public methods of <obj>.")]
public static MethodCallExpression AsString(this Expression obj) => Call(obj, nameof(ToString));

/// <summary>
Expand Down Expand Up @@ -1136,12 +1145,12 @@ public static ConditionalExpression Condition<TResult>(this Expression test, Exp
/// <param name="type">The type to be instantiated.</param>
/// <param name="args">The list of arguments to be passed into constructor.</param>
/// <returns>Instantiation expression.</returns>
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);
}
Expand All @@ -1155,6 +1164,7 @@ public static NewExpression New(this Type type, params Expression[] args)
/// <param name="expression">An expression representing object construction.</param>
/// <param name="bindings">A collection of members to initialize.</param>
/// <returns>Initialization expression.</returns>
[RequiresUnreferencedCode("Dynamic access to public properties of <expression>.")]
public static MemberInitExpression Init(this NewExpression expression, MemberBindings bindings)
=> Expression.MemberInit(expression, bindings.Bind(expression.Type));

Expand Down Expand Up @@ -1203,10 +1213,10 @@ internal static Expression AddEpilogue(this Expression expression, bool inferTyp
/// <param name="type">The expression representing the type to be instantiated.</param>
/// <param name="args">The list of arguments to be passed into constructor.</param>
/// <returns>Instantiation expression.</returns>
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(Activator))]
[RequiresUnreferencedCode("Dynamic access to public constructors of a type represented by <type>.")]
public static MethodCallExpression New(this Expression type, params Expression[] args)
{
var activate = typeof(Activator).GetMethod(nameof(Activator.CreateInstance), new[] { typeof(Type), typeof(object[]) });
var activate = typeof(Activator).GetMethod(nameof(Activator.CreateInstance), [typeof(Type), typeof(object[])]);
Debug.Assert(activate is not null);
return Expression.Call(activate, type, Expression.NewArrayInit(typeof(object), args));
}
Expand All @@ -1217,6 +1227,7 @@ public static MethodCallExpression New(this Expression type, params Expression[]
/// <param name="collection">The collection to iterate through.</param>
/// <param name="body">A delegate that is used to construct the body of the loop.</param>
/// <returns>The constructed loop.</returns>
[RequiresUnreferencedCode("Dynamic access to GetEnumerator method and IEnumerable<T> interfaces.")]
public static ForEachExpression ForEach(this Expression collection, ForEachExpression.Statement body)
=> ForEachExpression.Create(collection, body);

Expand Down Expand Up @@ -1453,18 +1464,20 @@ public static Expression AsOptional(this Expression expression)
/// </summary>
/// <param name="expression">The compound expression.</param>
/// <returns>The expression of type <see cref="Result{T}"/>.</returns>
[RequiresUnreferencedCode("Dynamic access to DotNext.Result<> data type")]
public static Expression AsResult(this Expression expression)
{
var exception = Expression.Parameter(typeof(Exception));
var ctor = typeof(Result<>).MakeGenericType(expression.Type).GetConstructor(new[] { expression.Type });
var ctor = typeof(Result<>).MakeGenericType(expression.Type).GetConstructor([expression.Type]);
Debug.Assert(ctor?.DeclaringType is not null);
var fallbackCtor = ctor.DeclaringType.GetConstructor(new[] { typeof(Exception) });
var fallbackCtor = ctor.DeclaringType.GetConstructor([typeof(Exception)]);
Debug.Assert(fallbackCtor is not null);
return Expression.TryCatch(
Expression.New(ctor, expression),
Expression.Catch(exception, Expression.New(fallbackCtor, exception)));
}

[RequiresUnreferencedCode("Dynamic access to indexer of <target>.")]
internal static IndexExpression MakeIndex(Expression target, Expression[] args)
{
// handle case for array
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;

Expand All @@ -13,6 +14,7 @@ namespace DotNext.Linq.Expressions;
/// Represents iteration over collection elements as expression.
/// </summary>
/// <seealso href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/foreach-in">foreach Statement</seealso>
[RequiresUnreferencedCode("Dynamic access to GetEnumerator (GetAsyncEnumerator) method and IEnumerable<T> (IAsyncEnumerable<T>) interfaces.")]
public sealed class ForEachExpression : CustomExpression, ILoopLabels
{
private const string EnumeratorVarName = "enumerator";
Expand Down Expand Up @@ -222,4 +224,4 @@ public override Expression Reduce()

return Reduce(moveNextCall, disposeCall);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,19 @@ private Expression MakePlainString()
case 0:
return Constant(Format);
case 1:
var formatMethod = typeof(string).GetMethod(nameof(string.Format), new[] { typeof(IFormatProvider), typeof(string), typeof(object) });
var formatMethod = typeof(string).GetMethod(nameof(string.Format), [typeof(IFormatProvider), typeof(string), typeof(object)]);
Debug.Assert(formatMethod is not null);
return Call(formatMethod, FormatProvider, Constant(Format), arguments[0]);
case 2:
formatMethod = typeof(string).GetMethod(nameof(string.Format), new[] { typeof(IFormatProvider), typeof(string), typeof(object), typeof(object) });
formatMethod = typeof(string).GetMethod(nameof(string.Format), [typeof(IFormatProvider), typeof(string), typeof(object), typeof(object)]);
Debug.Assert(formatMethod is not null);
return Call(formatMethod, FormatProvider, Constant(Format), FormatProvider, arguments[0], arguments[1]);
case 3:
formatMethod = typeof(string).GetMethod(nameof(string.Format), new[] { typeof(IFormatProvider), typeof(string), typeof(object), typeof(object), typeof(object) });
formatMethod = typeof(string).GetMethod(nameof(string.Format), [typeof(IFormatProvider), typeof(string), typeof(object), typeof(object), typeof(object)]);
Debug.Assert(formatMethod is not null);
return Call(formatMethod, FormatProvider, Constant(Format), FormatProvider, arguments[0], arguments[1], arguments[2]);
default:
formatMethod = typeof(string).GetMethod(nameof(string.Format), new[] { typeof(IFormatProvider), typeof(string), typeof(object[]) });
formatMethod = typeof(string).GetMethod(nameof(string.Format), [typeof(IFormatProvider), typeof(string), typeof(object[])]);
Debug.Assert(formatMethod is not null);
return Call(formatMethod, FormatProvider, Constant(Format), FormatProvider, NewArrayInit(typeof(object), arguments));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;
using Debug = System.Diagnostics.Debug;
Expand Down Expand Up @@ -64,6 +65,8 @@ public ItemIndexExpression(Expression value, bool fromEnd = false)
/// </summary>
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);

Expand All @@ -74,7 +77,7 @@ internal static Expression GetOffset(Expression index, Expression count)
/// <returns>Translated expression.</returns>
public override Expression Reduce()
{
ConstructorInfo? ctor = typeof(Index).GetConstructor(new[] { typeof(int), typeof(bool) });
ConstructorInfo? ctor = typeof(Index).GetConstructor([typeof(int), typeof(bool)]);
Debug.Assert(ctor is not null);
return New(ctor, conversionRequired ? Convert(Value, typeof(int)) : Value, Constant(IsFromEnd));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ public Expression Body
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicMethods, typeof(Monitor))]
public override Expression Reduce()
{
var monitorEnter = typeof(Monitor).GetMethod(nameof(Monitor.Enter), new[] { typeof(object) });
var monitorEnter = typeof(Monitor).GetMethod(nameof(Monitor.Enter), [typeof(object)]);
Debug.Assert(monitorEnter is not null);
var monitorExit = typeof(Monitor).GetMethod(nameof(Monitor.Exit), new[] { typeof(object) });
var monitorExit = typeof(Monitor).GetMethod(nameof(Monitor.Exit), [typeof(object)]);
Debug.Assert(monitorExit is not null);
var body = TryFinally(Body, Call(monitorExit, SyncRoot));
return assignment is null ?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;

Expand All @@ -21,7 +22,7 @@ public MemberBindings()
/// </summary>
/// <param name="target">The target type with the declared members.</param>
/// <returns>A list of bindings.</returns>
public IReadOnlyList<MemberAssignment> Bind(Type target)
public IReadOnlyList<MemberAssignment> Bind([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target)
{
const MemberTypes memberTypes = MemberTypes.Field | MemberTypes.Property;
const BindingFlags memberFlags = BindingFlags.Public | BindingFlags.Instance;
Expand Down
Loading
Loading