diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodInvoker.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodInvoker.CoreCLR.cs index 8468e8f9529e80..671206f80e821c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodInvoker.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodInvoker.CoreCLR.cs @@ -22,7 +22,7 @@ public MethodInvoker(MethodBase method, Signature signature) } else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke) { - // Always use emit invoke (if IsDynamicCodeCompiled == true); useful for testing. + // Always use emit invoke (if IsDynamicCodeSupported == true); useful for testing. _invoked = true; } } diff --git a/src/libraries/Common/tests/System/Reflection/InvokeEmitTests.cs b/src/libraries/Common/tests/System/Reflection/InvokeEmitTests.cs index d6345d3e923141..e95598dd47a666 100644 --- a/src/libraries/Common/tests/System/Reflection/InvokeEmitTests.cs +++ b/src/libraries/Common/tests/System/Reflection/InvokeEmitTests.cs @@ -34,8 +34,8 @@ public static void VerifyInvokeIsUsingEmit_Constructor() private static bool IsEmitInvokeSupported() { - // Emit is only used for Invoke when RuntimeFeature.IsDynamicCodeCompiled is true. - return RuntimeFeature.IsDynamicCodeCompiled; + // Emit is only used for Invoke when RuntimeFeature.IsDynamicCodeSupported is true. + return RuntimeFeature.IsDynamicCodeSupported; } private static string InterpretedMethodName => PlatformDetection.IsMonoRuntime ? diff --git a/src/libraries/Common/tests/System/Reflection/InvokeInterpretedTests.cs b/src/libraries/Common/tests/System/Reflection/InvokeInterpretedTests.cs index a197da333f16d5..355c154119cc13 100644 --- a/src/libraries/Common/tests/System/Reflection/InvokeInterpretedTests.cs +++ b/src/libraries/Common/tests/System/Reflection/InvokeInterpretedTests.cs @@ -8,7 +8,7 @@ namespace System.Reflection.Tests public class InvokeInterpretedTests { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50957", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/50957", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoAOT))] public static void VerifyInvokeIsUsingEmit_Method() { MethodInfo method = typeof(TestClassThatThrows).GetMethod(nameof(TestClassThatThrows.Throw))!; @@ -25,7 +25,7 @@ string InterpretedMethodName() => PlatformDetection.IsMonoRuntime ? } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/50957", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/50957", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoAOT))] public static void VerifyInvokeIsUsingEmit_Constructor() { ConstructorInfo ctor = typeof(TestClassThatThrows).GetConstructor(Type.EmptyTypes)!; diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs index 21b4646713bae5..953cc27850506b 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs @@ -129,10 +129,14 @@ public static ObjectFactory CreateFactory( Type[] argumentTypes) { #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP - if (!RuntimeFeature.IsDynamicCodeSupported) + if (!RuntimeFeature.IsDynamicCodeCompiled) { - // Create a reflection-based factory when dynamic code isn't supported, e.g. app is published with NativeAOT. - // Reflection-based factory is faster than interpreted expressions and doesn't pull in System.Linq.Expressions dependency. + // Create a reflection-based factory when dynamic code is not compiled\jitted as would be the case with + // NativeAOT, iOS or WASM. + // For NativeAOT and iOS, using the reflection-based factory is faster than reflection-fallback interpreted + // expressions and also doesn't pull in the large System.Linq.Expressions dependency. + // For WASM, although it has the ability to use expressions (with dynamic code) and interpet the dynamic code + // efficiently, the size savings of not using System.Linq.Expressions is more important than CPU perf. return CreateFactoryReflection(instanceType, argumentTypes); } #endif @@ -163,10 +167,9 @@ public static ObjectFactory Type[] argumentTypes) { #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP - if (!RuntimeFeature.IsDynamicCodeSupported) + if (!RuntimeFeature.IsDynamicCodeCompiled) { - // Create a reflection-based factory when dynamic code isn't supported, e.g. app is published with NativeAOT. - // Reflection-based factory is faster than interpreted expressions and doesn't pull in System.Linq.Expressions dependency. + // See the comment above in the non-generic CreateFactory() for why we use 'IsDynamicCodeCompiled' here. var factory = CreateFactoryReflection(typeof(T), argumentTypes); return (serviceProvider, arguments) => (T)factory(serviceProvider, arguments); } @@ -311,6 +314,11 @@ private static ObjectFactory CreateFactoryReflection( return (IServiceProvider serviceProvider, object?[]? arguments) => { + if (serviceProvider is null) + { + throw new ArgumentNullException(nameof(serviceProvider)); + } + object?[] constructorArguments = new object?[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ActivatorUtilitiesTests.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ActivatorUtilitiesTests.cs index c143e90a94301e..4c065b61bb2856 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ActivatorUtilitiesTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ActivatorUtilitiesTests.cs @@ -204,12 +204,12 @@ public void CreateFactory_CreatesFactoryMethod() #if NETCOREAPP [InlineData(false)] #endif - public void CreateFactory_RemoteExecutor_CreatesFactoryMethod(bool isDynamicCodeSupported) + public void CreateFactory_RemoteExecutor_CreatesFactoryMethod(bool useDynamicCode) { var options = new RemoteInvokeOptions(); - if (!isDynamicCodeSupported) + if (!useDynamicCode) { - options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", "false"); + DisableDynamicCode(options); } using var remoteHandle = RemoteExecutor.Invoke(static () => @@ -238,12 +238,12 @@ public void CreateFactory_RemoteExecutor_CreatesFactoryMethod(bool isDynamicCode #if NETCOREAPP [InlineData(false)] #endif - public void CreateFactory_RemoteExecutor_NullArguments_Throws(bool isDynamicCodeSupported) + public void CreateFactory_RemoteExecutor_NullArguments_Throws(bool useDynamicCode) { var options = new RemoteInvokeOptions(); - if (!isDynamicCodeSupported) + if (!useDynamicCode) { - options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", "false"); + DisableDynamicCode(options); } using var remoteHandle = RemoteExecutor.Invoke(static () => @@ -261,12 +261,12 @@ public void CreateFactory_RemoteExecutor_NullArguments_Throws(bool isDynamicCode #if NETCOREAPP [InlineData(false)] #endif - public void CreateFactory_RemoteExecutor_NoArguments_UseNullDefaultValue(bool isDynamicCodeSupported) + public void CreateFactory_RemoteExecutor_NoArguments_UseNullDefaultValue(bool useDynamicCode) { var options = new RemoteInvokeOptions(); - if (!isDynamicCodeSupported) + if (!useDynamicCode) { - options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", "false"); + DisableDynamicCode(options); } using var remoteHandle = RemoteExecutor.Invoke(static () => @@ -285,12 +285,12 @@ public void CreateFactory_RemoteExecutor_NoArguments_UseNullDefaultValue(bool is #if NETCOREAPP [InlineData(false)] #endif - public void CreateFactory_RemoteExecutor_NoArguments_ThrowRequiredValue(bool isDynamicCodeSupported) + public void CreateFactory_RemoteExecutor_NoArguments_ThrowRequiredValue(bool useDynamicCode) { var options = new RemoteInvokeOptions(); - if (!isDynamicCodeSupported) + if (!useDynamicCode) { - options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", "false"); + DisableDynamicCode(options); } using var remoteHandle = RemoteExecutor.Invoke(static () => @@ -309,12 +309,12 @@ public void CreateFactory_RemoteExecutor_NoArguments_ThrowRequiredValue(bool isD #if NETCOREAPP [InlineData(false)] #endif - public void CreateFactory_RemoteExecutor_NullArgument_UseDefaultValue(bool isDynamicCodeSupported) + public void CreateFactory_RemoteExecutor_NullArgument_UseDefaultValue(bool useDynamicCode) { var options = new RemoteInvokeOptions(); - if (!isDynamicCodeSupported) + if (!useDynamicCode) { - options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", "false"); + DisableDynamicCode(options); } using var remoteHandle = RemoteExecutor.Invoke(static () => @@ -333,12 +333,12 @@ public void CreateFactory_RemoteExecutor_NullArgument_UseDefaultValue(bool isDyn #if NETCOREAPP [InlineData(false)] #endif - public void CreateFactory_RemoteExecutor_NoParameters_Success(bool isDynamicCodeSupported) + public void CreateFactory_RemoteExecutor_NoParameters_Success(bool useDynamicCode) { var options = new RemoteInvokeOptions(); - if (!isDynamicCodeSupported) + if (!useDynamicCode) { - options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", "false"); + DisableDynamicCode(options); } using var remoteHandle = RemoteExecutor.Invoke(static () => @@ -351,6 +351,14 @@ public void CreateFactory_RemoteExecutor_NoParameters_Success(bool isDynamicCode Assert.NotNull(item); }, options); } + + private static void DisableDynamicCode(RemoteInvokeOptions options) + { + // We probably only need to set 'IsDynamicCodeCompiled' since only that is checked, + // but also set 'IsDynamicCodeSupported for correctness. + options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", "false"); + options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeCompiled", "false"); + } } internal class A { } @@ -361,8 +369,8 @@ internal class S { } internal class ClassWithABCS : ClassWithABC { public S S { get; } - public ClassWithABCS(A a, B b, C c, S s) : base (a, b, c) { S = s; } - public ClassWithABCS(A a, C c, S s) : this (a, null, c, s) { } + public ClassWithABCS(A a, B b, C c, S s) : base(a, b, c) { S = s; } + public ClassWithABCS(A a, C c, S s) : this(a, null, c, s) { } } internal class ClassWithABC_FirstConstructorWithAttribute : ClassWithABC @@ -374,9 +382,9 @@ public ClassWithABC_FirstConstructorWithAttribute(B b, C c) : this(null, b, c) { internal class ClassWithABC_LastConstructorWithAttribute : ClassWithABC { - public ClassWithABC_LastConstructorWithAttribute(B b, C c) : this(null, b, c) { } + public ClassWithABC_LastConstructorWithAttribute(B b, C c) : this(null, b, c) { } [ActivatorUtilitiesConstructor] - public ClassWithABC_LastConstructorWithAttribute(A a, B b, C c) : base(a, b , c) { } + public ClassWithABC_LastConstructorWithAttribute(A a, B b, C c) : base(a, b, c) { } } internal class FakeServiceProvider : IServiceProvider @@ -512,14 +520,14 @@ internal class ClassWithABC_DefaultConstructorFirst : ClassWithABC { public ClassWithABC_DefaultConstructorFirst() : base() { } public ClassWithABC_DefaultConstructorFirst(A a) : base(a) { } - public ClassWithABC_DefaultConstructorFirst(A a, B b) : base (a, b) { } - public ClassWithABC_DefaultConstructorFirst(A a, B b, C c) : base (a, b, c) { } + public ClassWithABC_DefaultConstructorFirst(A a, B b) : base(a, b) { } + public ClassWithABC_DefaultConstructorFirst(A a, B b, C c) : base(a, b, c) { } } internal class ClassWithABC_DefaultConstructorLast : ClassWithABC { - public ClassWithABC_DefaultConstructorLast(A a, B b, C c) : base (a, b, c) { } - public ClassWithABC_DefaultConstructorLast(A a, B b) : base (a, b) { } + public ClassWithABC_DefaultConstructorLast(A a, B b, C c) : base(a, b, c) { } + public ClassWithABC_DefaultConstructorLast(A a, B b) : base(a, b) { } public ClassWithABC_DefaultConstructorLast(A a) : base(a) { } public ClassWithABC_DefaultConstructorLast() : base() { } } @@ -533,3 +541,4 @@ public ClassWithStringDefaultValue(string text = "DEFAULT") } } } + 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 bef850f489fba1..f1abdcb46b0c21 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 @@ -939,6 +939,7 @@ Func CreateAotCompatibilityCallSiteFactory() RemoteInvokeOptions options = new RemoteInvokeOptions(); options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", "false"); + options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeCompiled", "false"); using RemoteInvokeHandle remoteHandle = RemoteExecutor.Invoke(() => { @@ -966,7 +967,7 @@ Func CreateAotCompatibilityCallSiteFactory() Assert.Equal(2, ((Struct1)callSite.Value).Value); }, options); - // Verify the above scenarios work when IsDynamicCodeSupported is not set + // Verify the above scenarios work when IsDynamicCodeSupported + IsDynamicCodeCompiled are not set Func callSiteFactory = CreateAotCompatibilityCallSiteFactory(); // Open Generics diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs index 9b581a305afdef..27ac00870c28a7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs @@ -25,7 +25,7 @@ public ConstructorInvoker(RuntimeConstructorInfo constructorInfo) } else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke) { - // Always use emit invoke (if IsDynamicCodeCompiled == true); useful for testing. + // Always use emit invoke (if IsDynamicCodeSupported == true); useful for testing. _invoked = true; } } @@ -53,7 +53,7 @@ public ConstructorInvoker(RuntimeConstructorInfo constructorInfo) } else { - if (RuntimeFeature.IsDynamicCodeCompiled) + if (RuntimeFeature.IsDynamicCodeSupported) { _invokeFunc = InvokerEmitUtil.CreateInvokeDelegate(_method); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs index 87bccf3173281f..c800f9e3ef02c0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvokerEmitUtil.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection.Emit; +using System.Runtime.CompilerServices; namespace System.Reflection { @@ -64,15 +65,19 @@ public static unsafe InvokeFunc CreateInvokeDelegate(MethodBase method) } } - // Invoke the method. // For CallStack reasons, don't inline target method. + // Mono interpreter does not support\need this. + if (RuntimeFeature.IsDynamicCodeCompiled) + { #if MONO - il.Emit(OpCodes.Call, Methods.DisableInline()); + il.Emit(OpCodes.Call, Methods.DisableInline()); #else - il.Emit(OpCodes.Call, Methods.NextCallReturnAddress()); - il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Call, Methods.NextCallReturnAddress()); + il.Emit(OpCodes.Pop); #endif + } + // Invoke the method. if (emitNew) { il.Emit(OpCodes.Newobj, (ConstructorInfo)method); diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs index a33ca063fa8777..15a0c195308286 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodInvoker.cs @@ -37,7 +37,7 @@ internal sealed partial class MethodInvoker } else { - if (RuntimeFeature.IsDynamicCodeCompiled) + if (RuntimeFeature.IsDynamicCodeSupported) { _invokeFunc = InvokerEmitUtil.CreateInvokeDelegate(_method); } diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/MethodInvoker.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/MethodInvoker.Mono.cs index 99df70de828fbb..b6a2a83b5514a3 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/MethodInvoker.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/MethodInvoker.Mono.cs @@ -18,7 +18,7 @@ public MethodInvoker(MethodBase method) } else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke) { - // Always use emit invoke (if IsDynamicCodeCompiled == true); useful for testing. + // Always use emit invoke (if IsDynamicCodeSupported == true); useful for testing. _invoked = true; } }