Skip to content

Commit

Permalink
Annotate System.Linq.Expressions with RequiresDynamicCode (dotnet#90456)
Browse files Browse the repository at this point in the history
* Annotate System.Linq.Expressions with RequiresDynamicCode

All this ended up with an RUC on Expression.Compile due to new arrays.
I could potentially silence this warning with a feature flag, but it is a
real risk, and one that users could maybe work around if alterted to the

Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com>
Co-authored-by: Stephen Toub <stoub@microsoft.com>
  • Loading branch information
3 people committed Aug 15, 2023
1 parent 333c2c7 commit 067a357
Show file tree
Hide file tree
Showing 50 changed files with 468 additions and 156 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -15,6 +16,26 @@ public static class AssertExtensions
{
private static bool IsNetFramework => RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework");


/// <summary>
/// Helper for AOT tests that verifies that the compile succeeds, or throws PlatformNotSupported
/// when AOT is enabled.
/// </summary>
public static void ThrowsOnAot<T>(Action action)
where T : Exception
{
#if NETCOREAPP // Dynamic code is always supported on .NET Framework
if (!RuntimeFeature.IsDynamicCodeSupported)
{
Assert.Throws<T>(action);
}
else
#endif
{
action();
}
}

public static void Throws<T>(Action action, string expectedMessage)
where T : Exception
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,23 @@ static MethodInfo GetArrayEmptyMethodInfo(Type elementType)
return ServiceLookupHelpers.GetArrayEmptyMethodInfo(elementType);
}

[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode",
Justification = "VerifyAotCompatibility ensures elementType is not a ValueType")]
static NewArrayExpression NewArrayInit(Type elementType, IEnumerable<Expression> expr)
{
Debug.Assert(!ServiceProvider.VerifyAotCompatibility || !elementType.IsValueType, "VerifyAotCompatibility=true will throw during building the IEnumerableCallSite if elementType is a ValueType.");

return Expression.NewArrayInit(elementType, expr);
}

if (callSite.ServiceCallSites.Length == 0)
{
return Expression.Constant(
GetArrayEmptyMethodInfo(callSite.ItemType)
.Invoke(obj: null, parameters: Array.Empty<object>()));
}

return Expression.NewArrayInit(
return NewArrayInit(
callSite.ItemType,
callSite.ServiceCallSites.Select(cs =>
Convert(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -607,9 +607,13 @@ protected Expression(System.Linq.Expressions.ExpressionType nodeType, System.Typ
public static System.Linq.Expressions.NewExpression New(System.Reflection.ConstructorInfo constructor, System.Collections.Generic.IEnumerable<System.Linq.Expressions.Expression>? arguments, params System.Reflection.MemberInfo[]? members) { throw null; }
public static System.Linq.Expressions.NewExpression New(System.Reflection.ConstructorInfo constructor, params System.Linq.Expressions.Expression[]? arguments) { throw null; }
public static System.Linq.Expressions.NewExpression New([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")]
public static System.Linq.Expressions.NewArrayExpression NewArrayBounds(System.Type type, System.Collections.Generic.IEnumerable<System.Linq.Expressions.Expression> bounds) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")]
public static System.Linq.Expressions.NewArrayExpression NewArrayBounds(System.Type type, params System.Linq.Expressions.Expression[] bounds) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")]
public static System.Linq.Expressions.NewArrayExpression NewArrayInit(System.Type type, System.Collections.Generic.IEnumerable<System.Linq.Expressions.Expression> initializers) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")]
public static System.Linq.Expressions.NewArrayExpression NewArrayInit(System.Type type, params System.Linq.Expressions.Expression[] initializers) { throw null; }
public static System.Linq.Expressions.UnaryExpression Not(System.Linq.Expressions.Expression expression) { throw null; }
public static System.Linq.Expressions.UnaryExpression Not(System.Linq.Expressions.Expression expression, System.Reflection.MethodInfo? method) { throw null; }
Expand Down Expand Up @@ -1028,6 +1032,7 @@ internal MethodCallExpression() { }
System.Linq.Expressions.Expression System.Linq.Expressions.IArgumentProvider.GetArgument(int index) { throw null; }
public System.Linq.Expressions.MethodCallExpression Update(System.Linq.Expressions.Expression? @object, System.Collections.Generic.IEnumerable<System.Linq.Expressions.Expression>? arguments) { throw null; }
}
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Creating arrays at runtime requires dynamic code generation.")]
public partial class NewArrayExpression : System.Linq.Expressions.Expression
{
internal NewArrayExpression() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,48 @@
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0016</DiagnosticId>
<Target>M:System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression,System.String,System.Type[],System.Linq.Expressions.Expression[]):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute]</Target>
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0016</DiagnosticId>
<Target>M:System.Linq.Expressions.Expression.Call(System.Type,System.String,System.Type[],System.Linq.Expressions.Expression[]):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute]</Target>
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0016</DiagnosticId>
<Target>M:System.Linq.Expressions.Expression.ListInit(System.Linq.Expressions.NewExpression,System.Collections.Generic.IEnumerable{System.Linq.Expressions.Expression}):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute]</Target>
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0016</DiagnosticId>
<Target>M:System.Linq.Expressions.Expression.ListInit(System.Linq.Expressions.NewExpression,System.Linq.Expressions.Expression[]):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute]</Target>
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0016</DiagnosticId>
<Target>M:System.Linq.Expressions.Expression.ListInit(System.Linq.Expressions.NewExpression,System.Reflection.MethodInfo,System.Collections.Generic.IEnumerable{System.Linq.Expressions.Expression}):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute]</Target>
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0016</DiagnosticId>
<Target>M:System.Linq.Expressions.Expression.ListInit(System.Linq.Expressions.NewExpression,System.Reflection.MethodInfo,System.Linq.Expressions.Expression[]):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute]</Target>
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0016</DiagnosticId>
<Target>M:System.Runtime.CompilerServices.CallSite`1.Create(System.Runtime.CompilerServices.CallSiteBinder):[T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute]</Target>
<Left>ref/net8.0/System.Linq.Expressions.dll</Left>
<Right>lib/net8.0/System.Linq.Expressions.dll</Right>
</Suppression>
<Suppression>
<DiagnosticId>CP0020</DiagnosticId>
<Target>M:System.Linq.Expressions.DynamicExpressionVisitor.#ctor</Target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -564,4 +564,7 @@
<data name="Arg_KeyNotFoundWithKey" xml:space="preserve">
<value>The given key '{0}' was not present in the dictionary.</value>
</data>
<data name="LiftingInExpressionRequiresDynamicCode" xml:space="preserve">
<value>Nullable lifting on non-primitive type '{0}' is only supported in expression trees when dynamic code generation is available.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<DefineConstants> $(DefineConstants);FEATURE_FAST_CREATE</DefineConstants>
<NoWarn>$(NoWarn);CA1859</NoWarn>
<EnableAOTAnalyzer>false</EnableAOTAnalyzer>
<!--
Disable constant propagation so that methods referenced from ILLink.Substitutions.xml don't get inlined
with a wrong value at library build time and the substitution can still be selected at publish time.
Expand Down Expand Up @@ -91,6 +90,7 @@
<Compile Include="System\Runtime\CompilerServices\CallSite.cs" />
<Compile Include="System\Runtime\CompilerServices\CallSiteBinder.cs" />
<Compile Include="System\Runtime\CompilerServices\CallSiteOps.cs" />
<Compile Include="System\Runtime\CompilerServices\CallSiteOpsReflectionCache.cs" />
<Compile Include="System\Runtime\CompilerServices\CallSiteHelpers.cs" />
<Compile Include="System\Runtime\CompilerServices\DynamicAttribute.cs" />
<Compile Include="System\Runtime\CompilerServices\DebugInfoGenerator.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -611,8 +611,7 @@ private DynamicMetaObject BuildCallMethodWithResult<TBinder>(MethodInfo method,
Expression.Call(
String_Format_String_ObjectArray,
Expression.Constant(convertFailed),
Expression.NewArrayInit(
typeof(object),
Expression.NewObjectArrayInit(
new TrueReadOnlyCollection<Expression>(
Expression.Condition(
Expression.Equal(resultMO.Expression, AstUtils.Null),
Expand Down Expand Up @@ -645,7 +644,7 @@ private DynamicMetaObject BuildCallMethodWithResult<TBinder>(MethodInfo method,
Expression.Block(
new TrueReadOnlyCollection<ParameterExpression>(result, callArgs),
new TrueReadOnlyCollection<Expression>(
method != DynamicObject_TryBinaryOperation ? Expression.Assign(callArgs, Expression.NewArrayInit(typeof(object), callArgsValue)) : Expression.Assign(callArgs, callArgsValue[0]),
method != DynamicObject_TryBinaryOperation ? Expression.Assign(callArgs, Expression.NewObjectArrayInit(callArgsValue)) : Expression.Assign(callArgs, callArgsValue[0]),
Expression.Condition(
Expression.Call(
GetLimitedSelf(),
Expand Down Expand Up @@ -705,7 +704,7 @@ private DynamicMetaObject CallMethodReturnLast<TBinder>(MethodInfo method, TBind
Expression.Block(
new TrueReadOnlyCollection<ParameterExpression>(result, callArgs),
new TrueReadOnlyCollection<Expression>(
Expression.Assign(callArgs, Expression.NewArrayInit(typeof(object), callArgsValue)),
Expression.Assign(callArgs, Expression.NewObjectArrayInit(callArgsValue)),
Expression.Condition(
Expression.Call(
GetLimitedSelf(),
Expand Down Expand Up @@ -768,7 +767,7 @@ private DynamicMetaObject CallMethodNoResult<TBinder>(MethodInfo method, TBinder
Expression.Block(
new TrueReadOnlyCollection<ParameterExpression>(callArgs),
new TrueReadOnlyCollection<Expression>(
Expression.Assign(callArgs, Expression.NewArrayInit(typeof(object), callArgsValue)),
Expression.Assign(callArgs, Expression.NewObjectArrayInit(callArgsValue)),
Expression.Condition(
Expression.Call(
GetLimitedSelf(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ internal static partial class CachedReflectionInfo
public static MethodInfo CallSiteOps_SetNotMatched =>
s_CallSiteOps_SetNotMatched ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.SetNotMatched))!;

private static MethodInfo? s_CallSiteOps_CreateMatchmaker;
public static MethodInfo CallSiteOps_CreateMatchmaker =>
s_CallSiteOps_CreateMatchmaker ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.CreateMatchmaker))!;

private static MethodInfo? s_CallSiteOps_GetMatch;
public static MethodInfo CallSiteOps_GetMatch =>
s_CallSiteOps_GetMatch ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.GetMatch))!;
Expand All @@ -33,34 +29,6 @@ internal static partial class CachedReflectionInfo
public static MethodInfo CallSiteOps_ClearMatch =>
s_CallSiteOps_ClearMatch ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.ClearMatch))!;

private static MethodInfo? s_CallSiteOps_UpdateRules;
public static MethodInfo CallSiteOps_UpdateRules =>
s_CallSiteOps_UpdateRules ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.UpdateRules))!;

private static MethodInfo? s_CallSiteOps_GetRules;
public static MethodInfo CallSiteOps_GetRules =>
s_CallSiteOps_GetRules ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.GetRules))!;

private static MethodInfo? s_CallSiteOps_GetRuleCache;
public static MethodInfo CallSiteOps_GetRuleCache =>
s_CallSiteOps_GetRuleCache ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.GetRuleCache))!;

private static MethodInfo? s_CallSiteOps_GetCachedRules;
public static MethodInfo CallSiteOps_GetCachedRules =>
s_CallSiteOps_GetCachedRules ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.GetCachedRules))!;

private static MethodInfo? s_CallSiteOps_AddRule;
public static MethodInfo CallSiteOps_AddRule =>
s_CallSiteOps_AddRule ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.AddRule))!;

private static MethodInfo? s_CallSiteOps_MoveRule;
public static MethodInfo CallSiteOps_MoveRule =>
s_CallSiteOps_MoveRule ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.MoveRule))!;

private static MethodInfo? s_CallSiteOps_Bind;
public static MethodInfo CallSiteOps_Bind =>
s_CallSiteOps_Bind ??= typeof(CallSiteOps).GetMethod(nameof(CallSiteOps.Bind))!;

private static MethodInfo? s_DynamicObject_TryGetMember;
public static MethodInfo DynamicObject_TryGetMember =>
s_DynamicObject_TryGetMember ??= typeof(DynamicObject).GetMethod(nameof(DynamicObject.TryGetMember))!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -55,7 +56,10 @@ internal static Delegate CreateObjectArrayDelegate(Type delegateType, Func<objec
{
if (CanEmitObjectArrayDelegate)
{
#pragma warning disable IL3050
// Suppress analyzer warnings since they don't currently support feature flags
return CreateObjectArrayDelegateRefEmit(delegateType, handler);
#pragma warning restore IL3050
}
else
{
Expand Down Expand Up @@ -100,8 +104,7 @@ public static TReturn FuncThunk2<T1, T2, TReturn>(Func<object?[], object> handle
return (TReturn)handler(new object?[]{t1, t2});
}

private static MethodInfo GetEmptyObjectArrayMethod() =>
typeof(Array).GetMethod(nameof(Array.Empty))!.MakeGenericMethod(typeof(object));
private static MethodInfo GetEmptyObjectArrayMethod() => ((Func<object[]>)Array.Empty<object>).GetMethodInfo();

private static MethodInfo[] GetActionThunks()
{
Expand All @@ -121,6 +124,7 @@ private static MethodInfo[] GetFuncThunks()

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod",
Justification = "The above ActionThunk and FuncThunk methods don't have trimming annotations.")]
[RequiresDynamicCode(Expression.GenericMethodRequiresDynamicCode)]
private static MethodInfo? GetCSharpThunk(Type returnType, bool hasReturnValue, ParameterInfo[] parameters)
{
try
Expand Down Expand Up @@ -193,6 +197,7 @@ private static MethodInfo[] GetFuncThunks()
// param0 = (T0)args[0]; // only generated for each byref argument
// }
// return (TRet)ret;
[RequiresDynamicCode("Ref emit requires dynamic code.")]
private static Delegate CreateObjectArrayDelegateRefEmit(Type delegateType, Func<object?[], object?> handler)
{
if (!s_thunks.TryGetValue(delegateType, out MethodInfo? thunkMethod))
Expand Down
Loading

0 comments on commit 067a357

Please sign in to comment.