diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.cs index 92a159382c79f..0b85156d60885 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.cs @@ -6,7 +6,6 @@ namespace Microsoft.Extensions.DependencyInjection { - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Using Microsoft.Extensions.DependencyInjection requires generating code dynamically at runtime. For example, when using enumerable and generic ValueType services.")] public partial class DefaultServiceProviderFactory : Microsoft.Extensions.DependencyInjection.IServiceProviderFactory { public DefaultServiceProviderFactory() { } @@ -16,11 +15,8 @@ public DefaultServiceProviderFactory(Microsoft.Extensions.DependencyInjection.Se } public static partial class ServiceCollectionContainerBuilderExtensions { - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Using Microsoft.Extensions.DependencyInjection requires generating code dynamically at runtime. For example, when using enumerable and generic ValueType services.")] public static Microsoft.Extensions.DependencyInjection.ServiceProvider BuildServiceProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Using Microsoft.Extensions.DependencyInjection requires generating code dynamically at runtime. For example, when using enumerable and generic ValueType services.")] public static Microsoft.Extensions.DependencyInjection.ServiceProvider BuildServiceProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Microsoft.Extensions.DependencyInjection.ServiceProviderOptions options) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Using Microsoft.Extensions.DependencyInjection requires generating code dynamically at runtime. For example, when using enumerable and generic ValueType services.")] public static Microsoft.Extensions.DependencyInjection.ServiceProvider BuildServiceProvider(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, bool validateScopes) { throw null; } } public sealed partial class ServiceProvider : System.IAsyncDisposable, System.IDisposable, System.IServiceProvider diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.csproj b/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.csproj index 1be6963319f1b..522569cbc5a0e 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/ref/Microsoft.Extensions.DependencyInjection.csproj @@ -12,10 +12,6 @@ - - - - diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/DefaultServiceProviderFactory.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/DefaultServiceProviderFactory.cs index 13863a4618a7d..7fe53e3aa8551 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/DefaultServiceProviderFactory.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/DefaultServiceProviderFactory.cs @@ -2,14 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Diagnostics.CodeAnalysis; namespace Microsoft.Extensions.DependencyInjection { /// /// Default implementation of . /// - [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] public class DefaultServiceProviderFactory : IServiceProviderFactory { private readonly ServiceProviderOptions _options; diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.DependencyInjection/src/Resources/Strings.resx index efbb7cc4a7451..a81b637ecf6b2 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/Resources/Strings.resx +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/Resources/Strings.resx @@ -180,4 +180,10 @@ Generic implementation type '{0}' has a DefaultConstructorConstraint ('new()' constraint), but the generic service type '{1}' doesn't. - + + Unable to create an Enumerable service of type '{0}' because it is a ValueType. Native code to support creating Enumerable services might not be available with native AOT. + + + Unable to create a generic service for type '{0}' because '{1}' is a ValueType. Native code to support creating generic services might not be available with native AOT. + + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceCollectionContainerBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceCollectionContainerBuilderExtensions.cs index 1cde6c4c69029..5b12c82b9d6e6 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceCollectionContainerBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceCollectionContainerBuilderExtensions.cs @@ -2,9 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using Microsoft.Extensions.DependencyInjection.ServiceLookup; namespace Microsoft.Extensions.DependencyInjection { @@ -18,8 +15,6 @@ public static class ServiceCollectionContainerBuilderExtensions /// /// The containing service descriptors. /// The . - - [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] public static ServiceProvider BuildServiceProvider(this IServiceCollection services) { return BuildServiceProvider(services, ServiceProviderOptions.Default); @@ -34,7 +29,6 @@ public static ServiceProvider BuildServiceProvider(this IServiceCollection servi /// true to perform check verifying that scoped services never gets resolved from root provider; otherwise false. /// /// The . - [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] public static ServiceProvider BuildServiceProvider(this IServiceCollection services, bool validateScopes) { return services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = validateScopes }); @@ -49,7 +43,6 @@ public static ServiceProvider BuildServiceProvider(this IServiceCollection servi /// Configures various service provider behaviors. /// /// The . - [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options) { if (services is null) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs index 22b3d4fd153ad..2e8527fb5f7e2 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs @@ -11,7 +11,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] internal sealed class CallSiteFactory : IServiceProviderIsService { private const int DefaultSlot = 0; @@ -244,8 +243,14 @@ private static bool AreCompatible(DynamicallyAccessedMemberTypes serviceDynamica serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { Type itemType = serviceType.GenericTypeArguments[0]; - CallSiteResultCacheLocation cacheLocation = CallSiteResultCacheLocation.Root; + if (ServiceProvider.VerifyAotCompatibility && itemType.IsValueType) + { + // NativeAOT apps are not able to make Enumerable of ValueType services + // since there is no guarantee the ValueType[] code has been generated. + throw new InvalidOperationException(SR.Format(SR.AotCannotCreateEnumerableValueType, itemType)); + } + CallSiteResultCacheLocation cacheLocation = CallSiteResultCacheLocation.Root; var callSites = new List(); // If item type is not generic we can safely use descriptor cache @@ -350,6 +355,9 @@ private static CallSiteResultCacheLocation GetCommonCacheLocation(CallSiteResult Justification = "MakeGenericType here is used to create a closed generic implementation type given the closed service type. " + "Trimming annotations on the generic types are verified when 'Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability' is set, which is set by default when PublishTrimmed=true. " + "That check informs developers when these generic types don't have compatible trimming annotations.")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "When ServiceProvider.VerifyAotCompatibility is true, which it is by default when PublishAot=true, " + + "this method ensures the generic types being created aren't using ValueTypes.")] private ServiceCallSite? TryCreateOpenGeneric(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot, bool throwOnConstraintViolation) { if (serviceType.IsConstructedGenericType && @@ -366,7 +374,13 @@ private static CallSiteResultCacheLocation GetCommonCacheLocation(CallSiteResult Type closedType; try { - closedType = descriptor.ImplementationType.MakeGenericType(serviceType.GenericTypeArguments); + Type[] genericTypeArguments = serviceType.GenericTypeArguments; + if (ServiceProvider.VerifyAotCompatibility) + { + VerifyOpenGenericAotCompatibility(serviceType, genericTypeArguments); + } + + closedType = descriptor.ImplementationType.MakeGenericType(genericTypeArguments); } catch (ArgumentException) { @@ -524,6 +538,24 @@ private ServiceCallSite CreateConstructorCallSite( return parameterCallSites; } + /// + /// Verifies none of the generic type arguments are ValueTypes. + /// + /// + /// NativeAOT apps are not guaranteed that the native code for the closed generic of ValueType + /// has been generated. To catch these problems early, this verification is enabled at development-time + /// to inform the developer early that this scenario will not work once AOT'd. + /// + private static void VerifyOpenGenericAotCompatibility(Type serviceType, Type[] genericTypeArguments) + { + foreach (Type typeArg in genericTypeArguments) + { + if (typeArg.IsValueType) + { + throw new InvalidOperationException(SR.Format(SR.AotCannotCreateGenericValueType, serviceType, typeArg)); + } + } + } public void Add(Type type, ServiceCallSite serviceCallSite) { diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs index f41e18238bcf6..08c74b10b26b4 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.ExceptionServices; @@ -10,7 +11,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] internal sealed class CallSiteRuntimeResolver : CallSiteVisitor { public static CallSiteRuntimeResolver Instance { get; } = new(); @@ -162,7 +162,7 @@ protected override object VisitServiceProvider(ServiceProviderCallSite servicePr protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context) { - var array = Array.CreateInstance( + Array array = CreateArray( enumerableCallSite.ItemType, enumerableCallSite.ServiceCallSites.Length); @@ -172,6 +172,15 @@ protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSit array.SetValue(value, index); } return array; + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "VerifyAotCompatibility ensures elementType is not a ValueType")] + static Array CreateArray(Type elementType, int length) + { + Debug.Assert(!ServiceProvider.VerifyAotCompatibility || !elementType.IsValueType, "VerifyAotCompatibility=true will throw during building the IEnumerableCallSite if elementType is a ValueType."); + + return Array.CreateInstance(elementType, length); + } } protected override object VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs index 45fe2c39ddf24..d7aa44d8487fb 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs @@ -8,12 +8,12 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] internal sealed class DynamicServiceProviderEngine : CompiledServiceProviderEngine { private readonly ServiceProvider _serviceProvider; - public DynamicServiceProviderEngine(ServiceProvider serviceProvider): base(serviceProvider) + [RequiresDynamicCode("Creates DynamicMethods")] + public DynamicServiceProviderEngine(ServiceProvider serviceProvider) : base(serviceProvider) { _serviceProvider = serviceProvider; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs index c8d4b7a6c57d8..af1cf5714c84e 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; @@ -11,7 +12,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] internal sealed class ExpressionResolverBuilder : CallSiteVisitor { private static readonly ParameterExpression ScopeParameter = Expression.Parameter(typeof(ServiceProviderEngineScope)); @@ -114,10 +114,19 @@ protected override Expression VisitFactory(FactoryCallSite factoryCallSite, obje protected override Expression VisitIEnumerable(IEnumerableCallSite callSite, object? context) { + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "VerifyAotCompatibility ensures elementType is not a ValueType")] + static MethodInfo GetArrayEmptyMethodInfo(Type elementType) + { + Debug.Assert(!ServiceProvider.VerifyAotCompatibility || !elementType.IsValueType, "VerifyAotCompatibility=true will throw during building the IEnumerableCallSite if elementType is a ValueType."); + + return ServiceLookupHelpers.GetArrayEmptyMethodInfo(elementType); + } + if (callSite.ServiceCallSites.Length == 0) { return Expression.Constant( - ServiceLookupHelpers.GetArrayEmptyMethodInfo(callSite.ItemType) + GetArrayEmptyMethodInfo(callSite.ItemType) .Invoke(obj: null, parameters: Array.Empty())); } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs index d179cb6f36a4e..8298bd30be3a3 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs @@ -10,7 +10,6 @@ internal sealed class ExpressionsServiceProviderEngine : ServiceProviderEngine { private readonly ExpressionResolverBuilder _expressionResolverBuilder; - [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] public ExpressionsServiceProviderEngine(ServiceProvider serviceProvider) { _expressionResolverBuilder = new ExpressionResolverBuilder(serviceProvider); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IEnumerableCallSite.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IEnumerableCallSite.cs index 5774a2c3212fc..75657584fd75f 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IEnumerableCallSite.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IEnumerableCallSite.cs @@ -3,11 +3,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] internal sealed class IEnumerableCallSite : ServiceCallSite { internal Type ItemType { get; } @@ -15,12 +15,22 @@ internal sealed class IEnumerableCallSite : ServiceCallSite public IEnumerableCallSite(ResultCache cache, Type itemType, ServiceCallSite[] serviceCallSites) : base(cache) { + Debug.Assert(!ServiceProvider.VerifyAotCompatibility || !itemType.IsValueType, "If VerifyAotCompatibility=true, an IEnumerableCallSite should not be created with a ValueType."); + ItemType = itemType; ServiceCallSites = serviceCallSites; } + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "When ServiceProvider.VerifyAotCompatibility is true, which it is by default when PublishAot=true, " + + "CallSiteFactory ensures ItemType is not a ValueType.")] public override Type ServiceType => typeof(IEnumerable<>).MakeGenericType(ItemType); - public override Type ImplementationType => ItemType.MakeArrayType(); + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "When ServiceProvider.VerifyAotCompatibility is true, which it is by default when PublishAot=true, " + + "CallSiteFactory ensures ItemType is not a ValueType.")] + public override Type ImplementationType => ItemType.MakeArrayType(); + public override CallSiteKind Kind { get; } = CallSiteKind.IEnumerable; } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs index b25e7f8216d0b..c8d099c6f4ce7 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs @@ -6,7 +6,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - [RequiresDynamicCode(ServiceProvider.RequiresDynamicCodeMessage)] internal sealed class RuntimeServiceProviderEngine : ServiceProviderEngine { public static RuntimeServiceProviderEngine Instance { get; } = new RuntimeServiceProviderEngine(); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs index dd9b1af11a55a..5954d086072db 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs @@ -16,8 +16,6 @@ namespace Microsoft.Extensions.DependencyInjection /// public sealed class ServiceProvider : IServiceProvider, IDisposable, IAsyncDisposable { - internal const string RequiresDynamicCodeMessage = "Using Microsoft.Extensions.DependencyInjection requires generating code dynamically at runtime. For example, when using enumerable and generic ValueType services."; - private readonly CallSiteValidator? _callSiteValidator; private readonly Func> _createServiceAccessor; @@ -36,7 +34,13 @@ public sealed class ServiceProvider : IServiceProvider, IDisposable, IAsyncDispo internal static bool VerifyOpenGenericServiceTrimmability { get; } = AppContext.TryGetSwitch("Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability", out bool verifyOpenGenerics) ? verifyOpenGenerics : false; - [RequiresDynamicCode(RequiresDynamicCodeMessage)] + internal static bool VerifyAotCompatibility => +#if NETFRAMEWORK || NETSTANDARD2_0 + false; +#else + !RuntimeFeature.IsDynamicCodeSupported; +#endif + internal ServiceProvider(ICollection serviceDescriptors, ServiceProviderOptions options) { // note that Root needs to be set before calling GetEngine(), because the engine may need to access Root @@ -157,7 +161,6 @@ private void ValidateService(ServiceDescriptor descriptor) } } - [RequiresDynamicCode(RequiresDynamicCodeMessage)] private Func CreateServiceAccessor(Type serviceType) { ServiceCallSite? callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain()); @@ -194,17 +197,16 @@ internal IServiceScope CreateScope() return new ServiceProviderEngineScope(this, isRootScope: false); } - [RequiresDynamicCode(RequiresDynamicCodeMessage)] private ServiceProviderEngine GetEngine() { ServiceProviderEngine engine; #if NETFRAMEWORK || NETSTANDARD2_0 - engine = new DynamicServiceProviderEngine(this); + engine = CreateDynamicEngine(); #else if (RuntimeFeature.IsDynamicCodeCompiled) { - engine = new DynamicServiceProviderEngine(this); + engine = CreateDynamicEngine(); } else { @@ -213,6 +215,10 @@ private ServiceProviderEngine GetEngine() } #endif return engine; + + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", + Justification = "CreateDynamicEngine won't be called when using NativeAOT.")] // see also https://github.com/dotnet/linker/issues/2715 + ServiceProviderEngine CreateDynamicEngine() => new DynamicServiceProviderEngine(this); } } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceLookup/CallSiteFactoryTest.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceLookup/CallSiteFactoryTest.cs index c304f1911db38..bef850f489fba 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceLookup/CallSiteFactoryTest.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceLookup/CallSiteFactoryTest.cs @@ -919,6 +919,84 @@ public void VerifyOpenGenericTrimmabilityChecks() }, options); } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] // RuntimeConfigurationOptions are not supported on .NET Framework (and neither is NativeAOT) + public void VerifyDynamicCodeNotSupportedChecks() + { + Func CreateAotCompatibilityCallSiteFactory() + { + ServiceDescriptor[] descriptors = new[] + { + new ServiceDescriptor(typeof(IFakeOpenGenericService<>), typeof(ClassWithNoConstraints<>), ServiceLifetime.Transient), + new ServiceDescriptor(typeof(IServiceWithTwoGenerics<,>), typeof(ServiceWithTwoGenericsValid<,>), ServiceLifetime.Transient), + + new ServiceDescriptor(typeof(Struct1), new Struct1(1)), + new ServiceDescriptor(typeof(Struct1), new Struct1(2)), + }; + + return GetCallSiteFactory(descriptors); + } + + RemoteInvokeOptions options = new RemoteInvokeOptions(); + options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", "false"); + + using RemoteInvokeHandle remoteHandle = RemoteExecutor.Invoke(() => + { + Func callSiteFactory = CreateAotCompatibilityCallSiteFactory(); + + // Verify open generics throw when passing ValueTypes + Assert.Throws(() => callSiteFactory(typeof(IFakeOpenGenericService))); + Assert.Throws(() => callSiteFactory(typeof(IFakeOpenGenericService))); + Assert.Throws(() => callSiteFactory(typeof(IServiceWithTwoGenerics))); + Assert.Throws(() => callSiteFactory(typeof(IServiceWithTwoGenerics))); + + ServiceCallSite callSite = callSiteFactory(typeof(IFakeOpenGenericService)); + Assert.Equal(CallSiteKind.Constructor, callSite.Kind); + Assert.Equal(typeof(ClassWithNoConstraints), callSite.ImplementationType); + + callSite = callSiteFactory(typeof(IServiceWithTwoGenerics)); + Assert.Equal(CallSiteKind.Constructor, callSite.Kind); + Assert.Equal(typeof(ServiceWithTwoGenericsValid), callSite.ImplementationType); + + // Verify Enumerable services throw when passing ValueTypes + Assert.Throws(() => callSiteFactory(typeof(IEnumerable))); + + callSite = callSiteFactory(typeof(Struct1)); + Assert.Equal(CallSiteKind.Constant, callSite.Kind); + Assert.Equal(2, ((Struct1)callSite.Value).Value); + }, options); + + // Verify the above scenarios work when IsDynamicCodeSupported is not set + Func callSiteFactory = CreateAotCompatibilityCallSiteFactory(); + + // Open Generics + ServiceCallSite callSite = callSiteFactory(typeof(IFakeOpenGenericService)); + Assert.Equal(CallSiteKind.Constructor, callSite.Kind); + Assert.Equal(typeof(ClassWithNoConstraints), callSite.ImplementationType); + + callSite = callSiteFactory(typeof(IFakeOpenGenericService)); + Assert.Equal(CallSiteKind.Constructor, callSite.Kind); + Assert.Equal(typeof(ClassWithNoConstraints), callSite.ImplementationType); + + callSite = callSiteFactory(typeof(IServiceWithTwoGenerics)); + Assert.Equal(CallSiteKind.Constructor, callSite.Kind); + Assert.Equal(typeof(ServiceWithTwoGenericsValid), callSite.ImplementationType); + + callSite = callSiteFactory(typeof(IServiceWithTwoGenerics)); + Assert.Equal(CallSiteKind.Constructor, callSite.Kind); + Assert.Equal(typeof(ServiceWithTwoGenericsValid), callSite.ImplementationType); + + // Enumerable + callSite = callSiteFactory(typeof(IEnumerable)); + Assert.Equal(CallSiteKind.IEnumerable, callSite.Kind); + IEnumerableCallSite enumerableCallSite = (IEnumerableCallSite)callSite; + Assert.Equal(2, enumerableCallSite.ServiceCallSites.Length); + Assert.Equal(CallSiteKind.Constant, enumerableCallSite.ServiceCallSites[0].Kind); + Assert.Equal(1, ((Struct1)enumerableCallSite.ServiceCallSites[0].Value).Value); + Assert.Equal(CallSiteKind.Constant, enumerableCallSite.ServiceCallSites[1].Kind); + Assert.Equal(2, ((Struct1)enumerableCallSite.ServiceCallSites[1].Value).Value); + } + private static Func GetCallSiteFactory(params ServiceDescriptor[] descriptors) { var collection = new ServiceCollection(); @@ -944,13 +1022,14 @@ private static ConstructorInfo GetConstructor(Type type, Type[] parameterTypes) c.GetParameters().Select(p => p.ParameterType), parameterTypes)); - private class Class1 { public Class1(Class2 c2) { } } private class Class2 { public Class2(Class3 c3) { } } private class Class3 { } private class Class4 { public Class4(Class3 c3) { } } private class Class5 { public Class5(Class2 c2) { } } + private record struct Struct1(int Value) { } + // Open generic private class ClassA { public ClassA(ClassB cb) { } } private class ClassB { public ClassB(ClassC cc) { } } diff --git a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs index 7910670857169..cc1d7d84b9bd1 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.cs @@ -25,22 +25,15 @@ public ConsoleLifetimeOptions() { } } public static partial class Host { - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Hosting uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public static Microsoft.Extensions.Hosting.HostApplicationBuilder CreateApplicationBuilder() { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Hosting uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public static Microsoft.Extensions.Hosting.HostApplicationBuilder CreateApplicationBuilder(string[]? args) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Hosting uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public static Microsoft.Extensions.Hosting.IHostBuilder CreateDefaultBuilder() { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Hosting uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public static Microsoft.Extensions.Hosting.IHostBuilder CreateDefaultBuilder(string[]? args) { throw null; } } public sealed partial class HostApplicationBuilder { - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Hosting uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public HostApplicationBuilder() { } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Hosting uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public HostApplicationBuilder(Microsoft.Extensions.Hosting.HostApplicationBuilderSettings? settings) { } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Hosting uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public HostApplicationBuilder(string[]? args) { } public Microsoft.Extensions.Configuration.ConfigurationManager Configuration { get { throw null; } } public Microsoft.Extensions.Hosting.IHostEnvironment Environment { get { throw null; } } @@ -61,7 +54,6 @@ public HostApplicationBuilderSettings() { } } public partial class HostBuilder : Microsoft.Extensions.Hosting.IHostBuilder { - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Hosting uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public HostBuilder() { } public System.Collections.Generic.IDictionary Properties { get { throw null; } } public Microsoft.Extensions.Hosting.IHost Build() { throw null; } @@ -76,7 +68,6 @@ public static partial class HostingHostBuilderExtensions { public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureAppConfiguration(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureDelegate) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureContainer(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureDelegate) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Hosting uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureDefaults(this Microsoft.Extensions.Hosting.IHostBuilder builder, string[]? args) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureHostOptions(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureOptions) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder ConfigureHostOptions(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureOptions) { throw null; } @@ -104,9 +95,7 @@ public static partial class HostingHostBuilderExtensions [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] public static Microsoft.Extensions.Hosting.IHostBuilder UseConsoleLifetime(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configureOptions) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder UseContentRoot(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, string contentRoot) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Hosting uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public static Microsoft.Extensions.Hosting.IHostBuilder UseDefaultServiceProvider(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configure) { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("Hosting uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public static Microsoft.Extensions.Hosting.IHostBuilder UseDefaultServiceProvider(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, System.Action configure) { throw null; } public static Microsoft.Extensions.Hosting.IHostBuilder UseEnvironment(this Microsoft.Extensions.Hosting.IHostBuilder hostBuilder, string environment) { throw null; } } diff --git a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.csproj b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.csproj index 663242e657617..634b13f75c45d 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting/ref/Microsoft.Extensions.Hosting.csproj @@ -13,10 +13,6 @@ - - - - diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs b/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs index 37c16325988f7..a836ec1c2276e 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Host.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics.CodeAnalysis; using System.IO; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -13,8 +12,6 @@ namespace Microsoft.Extensions.Hosting /// public static class Host { - internal const string RequiresDynamicCodeMessage = "Hosting uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime."; - /// /// Initializes a new instance of the class with pre-configured defaults. /// @@ -31,7 +28,6 @@ public static class Host /// /// /// The initialized . - [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static IHostBuilder CreateDefaultBuilder() => CreateDefaultBuilder(args: null); @@ -54,7 +50,6 @@ public static IHostBuilder CreateDefaultBuilder() => /// /// The command line args. /// The initialized . - [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static IHostBuilder CreateDefaultBuilder(string[]? args) { HostBuilder builder = new(); @@ -78,7 +73,6 @@ public static IHostBuilder CreateDefaultBuilder(string[]? args) /// enables scope validation on the dependency injection container when is 'Development' /// /// - [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static HostApplicationBuilder CreateApplicationBuilder() => new HostApplicationBuilder(); /// @@ -99,7 +93,6 @@ public static IHostBuilder CreateDefaultBuilder(string[]? args) /// /// /// The command line args. - [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static HostApplicationBuilder CreateApplicationBuilder(string[]? args) => new HostApplicationBuilder(args); } } diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs index 9962638c49d6a..f24e864c187ac 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.IO; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -46,7 +45,6 @@ public sealed class HostApplicationBuilder /// enables scope validation on the dependency injection container when is 'Development' /// /// - [RequiresDynamicCode(Host.RequiresDynamicCodeMessage)] public HostApplicationBuilder() : this(args: null) { @@ -70,7 +68,6 @@ public HostApplicationBuilder() /// /// /// The command line args. - [RequiresDynamicCode(Host.RequiresDynamicCodeMessage)] public HostApplicationBuilder(string[]? args) : this(new HostApplicationBuilderSettings { Args = args }) { @@ -80,7 +77,6 @@ public HostApplicationBuilder(string[]? args) /// Initializes a new instance of the . /// /// Settings controlling initial configuration and whether default settings should be used. - [RequiresDynamicCode(Host.RequiresDynamicCodeMessage)] public HostApplicationBuilder(HostApplicationBuilderSettings? settings) { settings ??= new HostApplicationBuilderSettings(); diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs index 4b1aa7d2952dd..4c015c536097b 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs @@ -41,7 +41,6 @@ public partial class HostBuilder : IHostBuilder /// /// Initializes a new instance of . /// - [RequiresDynamicCode(Host.RequiresDynamicCodeMessage)] public HostBuilder() { _serviceProviderFactory = new ServiceFactoryAdapter(new DefaultServiceProviderFactory()); diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs index a22f5bf31ac8b..c3f949a74db68 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Hosting/src/HostingHostBuilderExtensions.cs @@ -69,7 +69,6 @@ public static IHostBuilder UseContentRoot(this IHostBuilder hostBuilder, string /// The to configure. /// The delegate that configures the . /// The . - [RequiresDynamicCode(Host.RequiresDynamicCodeMessage)] public static IHostBuilder UseDefaultServiceProvider(this IHostBuilder hostBuilder, Action configure) => hostBuilder.UseDefaultServiceProvider((context, options) => configure(options)); @@ -79,7 +78,6 @@ public static IHostBuilder UseDefaultServiceProvider(this IHostBuilder hostBuild /// The to configure. /// The delegate that configures the . /// The . - [RequiresDynamicCode(Host.RequiresDynamicCodeMessage)] public static IHostBuilder UseDefaultServiceProvider(this IHostBuilder hostBuilder, Action configure) { return hostBuilder.UseServiceProviderFactory(context => @@ -192,7 +190,6 @@ public static IHostBuilder ConfigureContainer(this IHostBuild /// The existing builder to configure. /// The command line args. /// The same instance of the for chaining. - [RequiresDynamicCode(Host.RequiresDynamicCodeMessage)] public static IHostBuilder ConfigureDefaults(this IHostBuilder builder, string[]? args) { return builder.ConfigureHostConfiguration(config => ApplyDefaultHostConfiguration(config, args)) diff --git a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj index 5c15d36edcf2f..592f9e60338f4 100644 --- a/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj +++ b/src/libraries/Microsoft.Extensions.Hosting/src/Microsoft.Extensions.Hosting.csproj @@ -25,10 +25,6 @@ - - - - diff --git a/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs b/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs index dd65f6f0e04a6..5b7fade6eaa43 100644 --- a/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs +++ b/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.cs @@ -61,7 +61,6 @@ public LoggerFactory(System.Collections.Generic.IEnumerable providers, Microsoft.Extensions.Options.IOptionsMonitor filterOption, Microsoft.Extensions.Options.IOptions? options = null, Microsoft.Extensions.Logging.IExternalScopeProvider? scopeProvider = null) { } public void AddProvider(Microsoft.Extensions.Logging.ILoggerProvider provider) { } protected virtual bool CheckDisposed() { throw null; } - [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("LoggerFactory.Create uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public static Microsoft.Extensions.Logging.ILoggerFactory Create(System.Action configure) { throw null; } public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) { throw null; } public void Dispose() { } diff --git a/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.csproj b/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.csproj index 307fd0184170d..9ded83f82f6ee 100644 --- a/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/ref/Microsoft.Extensions.Logging.csproj @@ -7,10 +7,6 @@ - - - - diff --git a/src/libraries/Microsoft.Extensions.Logging/src/LoggerFactory.cs b/src/libraries/Microsoft.Extensions.Logging/src/LoggerFactory.cs index 39651be033007..315ea9115857a 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/LoggerFactory.cs +++ b/src/libraries/Microsoft.Extensions.Logging/src/LoggerFactory.cs @@ -103,7 +103,6 @@ public LoggerFactory(IEnumerable providers, IOptionsMonitor /// A delegate to configure the . /// The that was created. - [RequiresDynamicCode("LoggerFactory.Create uses Microsoft.Extensions.DependencyInjection, which may require generating code dynamically at runtime.")] public static ILoggerFactory Create(Action configure) { var serviceCollection = new ServiceCollection(); diff --git a/src/libraries/Microsoft.Extensions.Logging/src/Microsoft.Extensions.Logging.csproj b/src/libraries/Microsoft.Extensions.Logging/src/Microsoft.Extensions.Logging.csproj index d832ce579d99c..6d161e98cc0c6 100644 --- a/src/libraries/Microsoft.Extensions.Logging/src/Microsoft.Extensions.Logging.csproj +++ b/src/libraries/Microsoft.Extensions.Logging/src/Microsoft.Extensions.Logging.csproj @@ -30,10 +30,6 @@ - - - -