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

Annotate System.Linq.Expressions with RequiresDynamicCode #90456

Merged
merged 11 commits into from
Aug 15, 2023
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 @@ -43,7 +44,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 @@ -88,8 +92,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 @@ -109,6 +112,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 @@ -181,6 +185,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