Skip to content

Commit

Permalink
Add IsDynamicCodeSupported Feature Switch (dotnet#80246)
Browse files Browse the repository at this point in the history
* Add IsDynamicCodeSupported Feature Switch

This adds the ability to disable reflection emit for testing. It also allows for applications/libraries to simulate NativeAOT behavior (like switching on RuntimeFeature.IsDynamicCodeSupported) without actually publishing for NativeAOT.

Fix dotnet#39806

* Add IsDynamicCodeSupported feature switch support for Mono

* Remove featuredefault for IsDynamicCodeSupported so it isn't substituted during CoreLib's build.

* Add Intrinsic attribute back for Mono
  • Loading branch information
eerhardt authored Jan 9, 2023
1 parent 1c735ed commit df9de14
Show file tree
Hide file tree
Showing 26 changed files with 196 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,6 @@
<Compile Include="$(BclSourcesRoot)\System\Resources\ManifestBasedResourceGroveler.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\CastHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\ICastableHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeFeature.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeHelpers.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\ControlledExecution.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\DependentHandle.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
<linker>
<assembly fullname="System.Private.CoreLib">
<type fullname="System.Runtime.CompilerServices.RuntimeFeature" feature="System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported" featurevalue="true">
<method signature="System.Boolean get_IsDynamicCodeCompiled()" body="stub" value="true" />
<method signature="System.Boolean get_IsDynamicCodeSupported()" body="stub" value="true" />
</type>
<type fullname="System.Runtime.CompilerServices.RuntimeFeature" feature="System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported" featurevalue="false">
<method signature="System.Boolean get_IsDynamicCodeCompiled()" body="stub" value="false" />
<method signature="System.Boolean get_IsDynamicCodeSupported()" body="stub" value="false" />
</type>
<type fullname="System.StartupHookProvider" feature="System.StartupHookProvider.IsSupported" featurevalue="false">
<method signature="System.Boolean get_IsSupported()" body="stub" value="false" />
</type>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ internal AssemblyBuilder(AssemblyName name,
throw new InvalidOperationException();
}

EnsureDynamicCodeSupported();

_access = access;

_internalAssembly = CreateDynamicAssembly(assemblyLoadContext ?? AssemblyLoadContext.GetLoadContext(callingAssembly)!, name, access);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ public CustomAttributeBuilder(ConstructorInfo con, object?[] constructorArgs, Pr
ArgumentNullException.ThrowIfNull(namedFields);
ArgumentNullException.ThrowIfNull(fieldValues);

AssemblyBuilder.EnsureDynamicCodeSupported();

#pragma warning disable CA2208 // Instantiate argument exceptions correctly, combination of arguments used
if (namedProperties.Length != propertyValues.Length)
throw new ArgumentException(SR.Arg_ArrayLengthsDiffer, "namedProperties, propertyValues");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ private void Init(Module? mod)

if (m_module == null && mod != null)
throw new ArgumentException(SR.NotSupported_MustBeModuleBuilder);

AssemblyBuilder.EnsureDynamicCodeSupported();
}

[MemberNotNull(nameof(m_signature))]
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\RequiredMemberAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\RuntimeCompatibilityAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\RuntimeFeature.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\RuntimeFeature.NonNativeAot.cs" Condition="'$(FeatureNativeAot)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\RuntimeHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\RuntimeWrappedException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\CompilerServices\SkipLocalsInitAttribute.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;

namespace System.Reflection.Emit
{
Expand Down Expand Up @@ -38,5 +39,16 @@ public override string[] GetManifestResourceNames() =>

public override Stream? GetManifestResourceStream(Type type, string name) =>
throw new NotSupportedException(SR.NotSupported_DynamicAssembly);

internal static void EnsureDynamicCodeSupported()
{
if (!RuntimeFeature.IsDynamicCodeSupported)
{
ThrowDynamicCodeNotSupported();
}
}

private static void ThrowDynamicCodeNotSupported() =>
throw new PlatformNotSupportedException(SR.PlatformNotSupported_ReflectionEmit);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ private void Init(string name,
{
ArgumentNullException.ThrowIfNull(name);

AssemblyBuilder.EnsureDynamicCodeSupported();

if (attributes != (MethodAttributes.Static | MethodAttributes.Public) || callingConvention != CallingConventions.Standard)
throw new NotSupportedException(SR.NotSupported_DynamicMethodFlags);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Runtime.CompilerServices
{
public static partial class RuntimeFeature
{
public static bool IsDynamicCodeSupported
{
#if MONO
[Intrinsic] // the Mono AOT compiler will change this flag to false for FullAOT scenarios, otherwise this code is used
#endif
get;
} = AppContext.TryGetSwitch("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", out bool isDynamicCodeSupported) ? isDynamicCodeSupported : true;

public static bool IsDynamicCodeCompiled
{
#if MONO
[Intrinsic] // the Mono AOT compiler and Interpreter will change this flag to false for FullAOT and interpreted scenarios, otherwise this code is used
#endif
get => IsDynamicCodeSupported;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.RemoteExecutor;
using Xunit;

namespace System.Reflection.Emit.Tests
Expand Down Expand Up @@ -1069,6 +1070,28 @@ public void NamedPropertyAndPropertyValuesDifferentLengths_ThrowsArgumentExcepti
AssertExtensions.Throws<ArgumentException>(paramName, () => new CustomAttributeBuilder(con, new object[0], namedProperties, propertyValues, new FieldInfo[0], new object[0]));
}

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public static void ThrowsWhenDynamicCodeNotSupported()
{
RemoteInvokeOptions options = new RemoteInvokeOptions();
options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", false.ToString());

using RemoteInvokeHandle remoteHandle = RemoteExecutor.Invoke(static () =>
{
ConstructorInfo con = typeof(TestAttribute).GetConstructor(new Type[0]);
object[] constructorArgs = new object[0];
PropertyInfo[] namedProperties = Helpers.GetProperties(typeof(TestAttribute), nameof(TestAttribute.ObjectProperty));
object[] propertyValues = new object[] { new int[0, 0] };
FieldInfo[] namedFields = new FieldInfo[0];
object[] fieldValues = new object[0];

Assert.Throws<PlatformNotSupportedException>(() => new CustomAttributeBuilder(con, constructorArgs));
Assert.Throws<PlatformNotSupportedException>(() => new CustomAttributeBuilder(con, constructorArgs, namedFields, fieldValues));
Assert.Throws<PlatformNotSupportedException>(() => new CustomAttributeBuilder(con, constructorArgs, namedProperties, propertyValues));
Assert.Throws<PlatformNotSupportedException>(() => new CustomAttributeBuilder(con, constructorArgs, namedProperties, propertyValues, namedFields, fieldValues));
}, options);
}

private static Type CreateEnum(Type underlyingType, params object[] literalValues)
{
ModuleBuilder module = Helpers.DynamicModule();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.DotNet.RemoteExecutor;
using Xunit;

namespace System.Reflection.Emit.Tests
{
public class SignatureHelperDynamicCodeNotSupported
{
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public static void ThrowsWhenDynamicCodeNotSupported()
{
RemoteInvokeOptions options = new RemoteInvokeOptions();
options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", false.ToString());

using RemoteInvokeHandle remoteHandle = RemoteExecutor.Invoke(static () =>
{
Assert.Throws<PlatformNotSupportedException>(() => SignatureHelper.GetFieldSigHelper(null));
Assert.Throws<PlatformNotSupportedException>(() => SignatureHelper.GetLocalVarSigHelper());
Assert.Throws<PlatformNotSupportedException>(() => SignatureHelper.GetMethodSigHelper(CallingConventions.Any, typeof(int)));

// Mono always throws NotImplementedException - https://github.com/dotnet/runtime/issues/37794
if (!PlatformDetection.IsMonoRuntime)
{
Assert.Throws<PlatformNotSupportedException>(() => SignatureHelper.GetPropertySigHelper(null, typeof(string), new Type[] { typeof(string), typeof(int) }));
}
}, options);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<TestRuntime>true</TestRuntime>
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
</PropertyGroup>
<ItemGroup>
<Compile Include="ILGenerator\DeclareLocalTests.cs" />
Expand Down Expand Up @@ -32,6 +33,7 @@
<Compile Include="SignatureHelper\SignatureHelperGetMethodSigHelper.cs" />
<Compile Include="SignatureHelper\SignatureHelperGetPropertySigHelper.cs" />
<Compile Include="SignatureHelper\SignatureHelperGetSignature.cs" />
<Compile Include="SignatureHelper\SignatureHelperDynamicCodeNotSupported.cs" />
<Compile Include="SignatureHelper\SignatureHelperToString.cs" />
<Compile Include="CustomAttributeBuilderTests.cs" />
<Compile Include="Utilities.cs" />
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;
using Microsoft.DotNet.RemoteExecutor;
using Xunit;

namespace System.Reflection.Emit.Tests
Expand Down Expand Up @@ -173,5 +174,30 @@ public void InvalidOwner_ThrowsArgumentException(Type owner)
AssertExtensions.Throws<ArgumentException>(null, () => new DynamicMethod("Method", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(void), new Type[0], owner, true));
AssertExtensions.Throws<ArgumentException>(null, () => new DynamicMethod("Method", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(void), new Type[0], owner, false));
}

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public static void ThrowsWhenDynamicCodeNotSupported()
{
RemoteInvokeOptions options = new RemoteInvokeOptions();
options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", false.ToString());

using RemoteInvokeHandle remoteHandle = RemoteExecutor.Invoke(static () =>
{
Module module = typeof(TestClass).GetTypeInfo().Module;
string name = "Method";
Type returnType = typeof(void);
Type[] parameterTypes = null;
Type owner = typeof(TestClass);

Assert.Throws<PlatformNotSupportedException>(() => new DynamicMethod(name, returnType, parameterTypes));
Assert.Throws<PlatformNotSupportedException>(() => new DynamicMethod(name, returnType, parameterTypes, true));
Assert.Throws<PlatformNotSupportedException>(() => new DynamicMethod(name, returnType, parameterTypes, module));
Assert.Throws<PlatformNotSupportedException>(() => new DynamicMethod(name, returnType, parameterTypes, owner));
Assert.Throws<PlatformNotSupportedException>(() => new DynamicMethod(name, returnType, parameterTypes, module, true));
Assert.Throws<PlatformNotSupportedException>(() => new DynamicMethod(name, returnType, parameterTypes, owner, true));
Assert.Throws<PlatformNotSupportedException>(() => new DynamicMethod(name, MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, returnType, parameterTypes, module, true));
Assert.Throws<PlatformNotSupportedException>(() => new DynamicMethod(name, MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, returnType, parameterTypes, owner, true));
}, options);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<TestRuntime>true</TestRuntime>
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
</PropertyGroup>
<ItemGroup>
<Compile Include="DynamicILInfoTests.cs" />
Expand Down
16 changes: 16 additions & 0 deletions src/libraries/System.Reflection.Emit/tests/AssemblyBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.DotNet.RemoteExecutor;
using Xunit;

namespace System.Reflection.Emit.Tests
Expand Down Expand Up @@ -442,5 +443,20 @@ public void DefineDynamicAssembly_AssemblyBuilderLocationIsEmpty_InternalAssembl
Assert.Empty(internalAssemblyBuilder.Location);
}

[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
public static void ThrowsWhenDynamicCodeNotSupported()
{
RemoteInvokeOptions options = new RemoteInvokeOptions();
options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", false.ToString());

using RemoteInvokeHandle remoteHandle = RemoteExecutor.Invoke(static () =>
{
var assemblyName = new AssemblyName("TestName");
AssemblyBuilderAccess access = AssemblyBuilderAccess.Run;

Assert.Throws<PlatformNotSupportedException>(() => AssemblyBuilder.DefineDynamicAssembly(assemblyName, access));
Assert.Throws<PlatformNotSupportedException>(() => AssemblyBuilder.DefineDynamicAssembly(assemblyName, access, assemblyAttributes: null));
}, options);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<TestRuntime>true</TestRuntime>
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
</PropertyGroup>
<ItemGroup>
<Compile Include="AssemblyBuilderTests.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.DotNet.RemoteExecutor;
using Xunit;

namespace System.Runtime.CompilerServices.Tests
Expand Down Expand Up @@ -70,5 +71,26 @@ public static void StaticDataMatchesDynamicProbing(string probedValue)
{
Assert.True(RuntimeFeature.IsSupported(probedValue));
}

[ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[InlineData(true)]
[InlineData(false)]
public static void DynamicCode_ContextSwitch(bool isDynamicCodeSupported)
{
RemoteInvokeOptions options = new RemoteInvokeOptions();
options.RuntimeConfigurationOptions.Add("System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported", isDynamicCodeSupported.ToString());

// IsDynamicCodeCompiled on Mono interpreter always returns false
bool isDynamicCodeCompiled = PlatformDetection.IsMonoInterpreter ? false : isDynamicCodeSupported;

using RemoteInvokeHandle remoteHandle = RemoteExecutor.Invoke(static (isDynamicCodeSupportedString, isDynamicCodeCompiledString) =>
{
bool isDynamicCodeSupported = bool.Parse(isDynamicCodeSupportedString);
Assert.Equal(isDynamicCodeSupported, RuntimeFeature.IsDynamicCodeSupported);

bool isDynamicCodeCompiled = bool.Parse(isDynamicCodeCompiledString);
Assert.Equal(isDynamicCodeCompiled, RuntimeFeature.IsDynamicCodeCompiled);
}, isDynamicCodeSupported.ToString(), isDynamicCodeCompiled.ToString(), options);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@
<Compile Include="$(BclSourcesRoot)\System\Runtime\JitInfo.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\JitHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeHelpers.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\RuntimeFeature.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\GCHandle.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\Marshal.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\MemoryMarshal.Mono.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ public sealed partial class AssemblyBuilder : Assembly
[DynamicDependency(nameof(access))] // Automatically keeps all previous fields too due to StructLayout
private AssemblyBuilder(AssemblyName n, AssemblyBuilderAccess access)
{
EnsureDynamicCodeSupported();

aname = (AssemblyName)n.Clone();

if (!Enum.IsDefined(typeof(AssemblyBuilderAccess), access))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ private void Initialize(ConstructorInfo con, object?[] constructorArgs,
ArgumentNullException.ThrowIfNull(namedFields);
ArgumentNullException.ThrowIfNull(fieldValues);

AssemblyBuilder.EnsureDynamicCodeSupported();

if (con.GetParametersCount() != constructorArgs.Length)
throw new ArgumentException(SR.Argument_BadParameterCountsForConstructor);
if (namedProperties.Length != propertyValues.Length)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ internal enum SignatureHelperType
[DynamicDependency(nameof(modopts))] // Automatically keeps all previous fields too due to StructLayout
internal SignatureHelper(ModuleBuilder? module, SignatureHelperType type)
{
AssemblyBuilder.EnsureDynamicCodeSupported();

this.type = type;
this.module = module;
}
Expand Down

This file was deleted.

Loading

0 comments on commit df9de14

Please sign in to comment.