Skip to content
This repository has been archived by the owner on Nov 1, 2020. It is now read-only.

Add a mode that disables reflection #7208

Merged
merged 7 commits into from
Apr 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Documentation/using-corert/optimizing-corert.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ By default, the compiler tries to maximize compatibility with existing .NET code
* `<RootAllApplicationAssemblies>false</RootAllApplicationAssemblies>`: this disables the compiler behavior where all code in the application assemblies is considered dynamically reachable. Leaving this option at the default value (`true`) has a significant effect on the size of the resulting executable because it prevents removal of unused code that would otherwise happen. The default value ensures compatibility with reflection-heavy applications.
* `<IlcGenerateCompleteTypeMetadata>false</IlcGenerateCompleteTypeMetadata>`: this disables generation of complete type metadata. This is a compilation mode that prevents a situation where some members of a type are visible to reflection at runtime, but others aren't, because they weren't compiled.
* `<IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>`: this disables generation of stack trace metadata that provides textual names in stack traces. This is for example the text string one gets by calling `Exception.ToString()` on a caught exception. With this option disabled, stack traces will still be generated, but will be based on reflection metadata alone (they might be less complete).
* `<IlcDisableReflection>true</IlcDisableReflection>`: this completely disables the reflection metadata generation. Very basic reflection will still work (you can still use `typeof`, call `Object.GetType()`, compare the results, and query for basic properties such as `Type.IsValueType` or `Type.BaseType`), but most of the reflection stack will no longer work (no way to query/access methods and fields on types, or get names of types). This mode is experimental.

## Options related to code generation
* `<IlcOptimizationPreference>Speed</IlcOptimizationPreference>`: when generating optimized code, favor code execution speed.
Expand Down
2 changes: 1 addition & 1 deletion Documentation/using-corert/reflection-in-aot-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@ The compiler can build insights into how reflection is used by analyzing the use

### Assume nothing is accessed dynamically ###

In CoreRT, reflection metadata (names of types, list of their methods, fields, signatures, etc.) is _optional_. The CoreRT runtime has its own minimal version of the metadata that represents the minimum required to execute managed code (think: base type and list of interfaces, offsets to GC pointers within an instance of the type, pointer to the finalizer, etc.). The metadata used by the reflection subsystem within the base class libraries is only used by the reflection stack and is not necessary to execute non-reflection code. For a .NET app that doesn't use reflection, the compiler can skip generating the reflection metadata completely. This option is currently [not publicly exposed](https://github.com/dotnet/corert/issues/6897), but it exists. People who would like to totally minimize the size of their applications or obfuscate their code could be interested in this option.
In CoreRT, reflection metadata (names of types, list of their methods, fields, signatures, etc.) is _optional_. The CoreRT runtime has its own minimal version of the metadata that represents the minimum required to execute managed code (think: base type and list of interfaces, offsets to GC pointers within an instance of the type, pointer to the finalizer, etc.). The metadata used by the reflection subsystem within the base class libraries is only used by the reflection stack and is not necessary to execute non-reflection code. For a .NET app that doesn't use reflection, the compiler can skip generating the reflection metadata completely. People who would like to totally minimize the size of their applications or obfuscate their code could be interested in this option, although not much existing real world code would be expected to work with this (including a lot of the framework code).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I expect that Unity tiny mode will come with similar limitations.

15 changes: 11 additions & 4 deletions src/BuildIntegration/Microsoft.NETCore.Native.targets
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ See the LICENSE file in the project root for more information.
<ItemGroup Condition="'$(ExperimentalDynamicCodeSupport)' != 'true'">
<AutoInitializedAssemblies Include="System.Private.StackTraceMetadata" />
<AutoInitializedAssemblies Include="System.Private.TypeLoader" />
<AutoInitializedAssemblies Include="System.Private.Reflection.Execution" />
<AutoInitializedAssemblies Include="System.Private.Reflection.Execution" Condition="$(IlcDisableReflection) != 'true'" />
<AutoInitializedAssemblies Include="System.Private.DisabledReflection" Condition="$(IlcDisableReflection) == 'true'" />
<AutoInitializedAssemblies Include="System.Private.Interop" />
</ItemGroup>

Expand Down Expand Up @@ -202,13 +203,19 @@ See the LICENSE file in the project root for more information.
<IlcArg Include="@(AutoInitializedAssemblies->'--initassembly:%(Identity)')" />
<IlcArg Include="@(AppContextSwitchOverrides->'--appcontextswitch:%(Identity)')" />
<IlcArg Condition="$(ServerGarbageCollection) != ''" Include="--runtimeopt:RH_UseServerGC=1" />
<IlcArg Condition="$(IlcGenerateCompleteTypeMetadata) == 'true'" Include="--completetypemetadata" />
<IlcArg Condition="$(IlcGenerateCompleteTypeMetadata) == 'true' and $(IlcDisableReflection) != 'true'" Include="--completetypemetadata" />
<IlcArg Condition="$(IlcGenerateStackTraceData) == 'true'" Include="--stacktracedata" />
<IlcArg Condition="$(RootAllApplicationAssemblies) == 'true'" Include="--rootallapplicationassemblies" />
<IlcArg Condition="$(IlcScanReflection) == 'true'" Include="--scanreflection" />
<IlcArg Condition="$(RootAllApplicationAssemblies) == 'true' and $(IlcDisableReflection) != 'true'" Include="--rootallapplicationassemblies" />
<IlcArg Condition="$(IlcScanReflection) == 'true' and $(IlcDisableReflection) != 'true'" Include="--scanreflection" />
<IlcArg Condition="$(IlcFoldIdenticalMethodBodies) == 'true'" Include="--methodbodyfolding" />
<IlcArg Condition="$(Optimize) == 'true' and $(IlcOptimizationPreference) == 'Size'" Include="--Os" />
<IlcArg Condition="$(Optimize) == 'true' and $(IlcOptimizationPreference) == 'Speed'" Include="--Ot" />
<IlcArg Condition="$(IlcDisableReflection) == 'true'" Include="--disablereflection" />
<IlcArg Condition="$(IlcDisableReflection) == 'true'" Include="--removefeature:EventSource" />
<IlcArg Condition="$(IlcDisableReflection) == 'true'" Include="--removefeature:FrameworkStrings" />

<!-- Comparers don't currently work with reflection disabled. https://github.com/dotnet/corert/pull/7208 -->
<IlcArg Condition="$(IlcDisableReflection) == 'true'" Include="--removefeature:Comparers" />
</ItemGroup>

<MakeDir Directories="$(NativeIntermediateOutputPath)" />
Expand Down
4 changes: 3 additions & 1 deletion src/ILCompiler/src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ internal class Program
private string _mapFileName;
private string _metadataLogFileName;
private bool _noMetadataBlocking;
private bool _disableReflection;
private bool _completeTypesMetadata;
private bool _scanReflection;
private bool _methodBodyFolding;
Expand Down Expand Up @@ -176,6 +177,7 @@ private ArgumentSyntax ParseCommandLine(string[] args)
syntax.DefineOption("map", ref _mapFileName, "Generate a map file");
syntax.DefineOption("metadatalog", ref _metadataLogFileName, "Generate a metadata log file");
syntax.DefineOption("nometadatablocking", ref _noMetadataBlocking, "Ignore metadata blocking for internal implementation details");
syntax.DefineOption("disablereflection", ref _disableReflection, "Disable generation of reflection metadata");
syntax.DefineOption("completetypemetadata", ref _completeTypesMetadata, "Generate complete metadata for types");
syntax.DefineOption("scanreflection", ref _scanReflection, "Scan IL for reflection patterns");
syntax.DefineOption("scan", ref _useScanner, "Use IL scanner to generate optimized code (implied by -O)");
Expand Down Expand Up @@ -524,7 +526,7 @@ private int Run(string[] args)

DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy = new DefaultDynamicInvokeThunkGenerationPolicy();

bool supportsReflection = !_isReadyToRunCodeGen && _systemModuleName == DefaultSystemModule;
bool supportsReflection = !_disableReflection && !_isReadyToRunCodeGen && _systemModuleName == DefaultSystemModule;

MetadataManager metadataManager;
if (_isReadyToRunCodeGen)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ public abstract class ReflectionExecutionDomainCallbacks

public abstract string GetBetterDiagnosticInfoIfAvailable(RuntimeTypeHandle runtimeTypeHandle);
public abstract MethodBase GetMethodBaseFromStartAddressIfAvailable(IntPtr methodStartAddress);

#if PROJECTN
public abstract int ValueTypeGetHashCodeUsingReflection(object valueType);
public abstract bool ValueTypeEqualsUsingReflection(object left, object right);
#endif

/// <summary>
/// Retrieves the default value for a parameter of a method.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Reflection;

using Internal.Runtime.Augments;

namespace Internal.Reflection
{
internal class ReflectionExecutionDomainCallbacksImplementation : ReflectionExecutionDomainCallbacks
{
public override Exception CreateMissingMetadataException(Type typeWithMissingMetadata) => throw new NotImplementedException();
public override Type GetArrayTypeForHandle(RuntimeTypeHandle typeHandle) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle);
public override string GetBetterDiagnosticInfoIfAvailable(RuntimeTypeHandle runtimeTypeHandle) => null;
public override Type GetByRefTypeForHandle(RuntimeTypeHandle typeHandle) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle);
public override Type GetConstructedGenericTypeForHandle(RuntimeTypeHandle typeHandle) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle);
public override MethodInfo GetDelegateMethod(Delegate del) => throw new NotSupportedException(SR.Reflection_Disabled);
public override Exception GetExceptionForHR(int hr) => throw new NotImplementedException();
public override Type GetMdArrayTypeForHandle(RuntimeTypeHandle typeHandle, int rank) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle);
public override MethodBase GetMethodBaseFromStartAddressIfAvailable(IntPtr methodStartAddress) => null;
public override Type GetNamedTypeForHandle(RuntimeTypeHandle typeHandle, bool isGenericTypeDefinition) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle);
public override Type GetPointerTypeForHandle(RuntimeTypeHandle typeHandle) => RuntimeTypeInfo.GetRuntimeTypeInfo(typeHandle);
public override Type GetType(string typeName, Func<AssemblyName, Assembly> assemblyResolver, Func<Assembly, string, bool, Type> typeResolver, bool throwOnError, bool ignoreCase, string defaultAssembly) => throw new NotSupportedException(SR.Reflection_Disabled);
public override RuntimeTypeHandle GetTypeHandleIfAvailable(Type type) => type.TypeHandle;
public override bool IsReflectionBlocked(RuntimeTypeHandle typeHandle) => false;
public override bool SupportsReflection(Type type) => false;
public override bool TryGetDefaultParameterValue(object defaultParametersContext, RuntimeTypeHandle thType, int argIndex, out object defaultValue)
{
defaultValue = null;
return false;
}
public override IntPtr TryGetStaticClassConstructionContext(RuntimeTypeHandle runtimeTypeHandle) => throw new NotSupportedException(SR.Reflection_Disabled);
}
}
Loading