未验证 提交 10288c96 编写于 作者: M Michal Strehovský 提交者: GitHub

Enable IlcTrimMetadata by default (#70201)

Enables more aggressive metadata stripping by default. In this mode, the AOT compiler only generates reflection metadata for artifacts that were deemed reflection-visible by either dataflow analysis, or user inputs. Previously we would consider everything that got generated reflection-visible.

* Change the defaults within the compiler (making the conservative mode opt-in).
* Add tests for scenarios in https://github.com/dotnet/runtimelab/issues/656 (resolves dotnet/runtimelab#656).
* Update tests
* Do more marking where it was necessary
上级 06c7957e
......@@ -232,14 +232,14 @@ The .NET Foundation licenses this file to you under the MIT license.
<IlcArg Condition="$(Optimize) == 'true' and $(IlcOptimizationPreference) == 'Size'" Include="--Os" />
<IlcArg Condition="$(Optimize) == 'true' and $(IlcOptimizationPreference) == 'Speed'" Include="--Ot" />
<IlcArg Condition="$(IlcInstructionSet) != ''" Include="--instructionset:$(IlcInstructionSet)" />
<IlcArg Condition="$(IlcDisableReflection) == 'true'" Include="--disablereflection" />
<IlcArg Condition="$(IlcDisableReflection) == 'true'" Include="--reflectiondata:none" />
<IlcArg Condition="$(IlcDisableReflection) == 'true'" Include="--feature:System.Collections.Generic.DefaultComparers=false" />
<IlcArg Condition="$(IlcSingleThreaded) == 'true'" Include="--parallelism:1" />
<IlcArg Condition="$(IlcSystemModule) != ''" Include="--systemmodule:$(IlcSystemModule)" />
<IlcArg Condition="$(IlcDumpIL) == 'true'" Include="--ildump:$(NativeIntermediateOutputPath)%(ManagedBinary.Filename).il" />
<IlcArg Condition="$(NoWarn) != ''" Include='--nowarn:"$([MSBuild]::Escape($(NoWarn)))"' />
<IlcArg Condition="$(TrimmerSingleWarn) == 'true'" Include="--singlewarn" />
<IlcArg Condition="$(IlcTrimMetadata) == 'true'" Include="--reflectedonly" />
<IlcArg Condition="$(IlcTrimMetadata) == 'false'" Include="--reflectiondata:all" />
<IlcArg Condition="'$(ControlFlowGuard)' == 'Guard' and '$(TargetOS)' == 'windows'" Include="--guard:cf" />
<IlcArg Include="@(_IlcRootedAssemblies->'--root:%(Identity)')" />
<IlcArg Include="@(_IlcConditionallyRootedAssemblies->'--conditionalroot:%(Identity)')" />
......
......@@ -12,12 +12,6 @@ To specify a switch, add a new property to your project file with one or more of
under the `<Project>` node of your project file.
## Options related to library features
Native AOT supports enabling and disabling all [documented framework library features](https://docs.microsoft.com/en-us/dotnet/core/deploying/trimming-options#trimming-framework-library-features). For example, to remove globalization specific code and data, add a `<InvariantGlobalization>true</InvariantGlobalization>` property to your project. Disabling a framework feature (or enabling a minimal mode of the feature) can result in significant size savings.
🛈 Native AOT difference: The `EnableUnsafeBinaryFormatterSerialization` framework switch is already set to the optimal value of `false` (removing the support for [obsolete](https://github.com/dotnet/designs/blob/21b274dbc21e4ae54b7e4c5dbd5ef31e439e78db/accepted/2020/better-obsoletion/binaryformatter-obsoletion.md) binary serialization).
## Options related to trimming
The Native AOT compiler supports the [documented options](https://docs.microsoft.com/en-us/dotnet/core/deploying/trim-self-contained) for removing unused code (trimming). By default, the compiler tries to very conservatively remove some of the unused code.
......@@ -26,16 +20,21 @@ The Native AOT compiler supports the [documented options](https://docs.microsoft
By default, the compiler tries to maximize compatibility with existing .NET code at the expense of compilation speed and size of the output executable. This allows people to use their existing code that worked well in a fully dynamic mode without hitting issues caused by trimming. To read more about reflection, see the [Reflection in AOT mode](reflection-in-aot-mode.md) document.
🛈 Native AOT difference: the `TrimMode` of framework assemblies is set to `link` by default. To compile entire framework assemblies, use `TrimmerRootAssembly` to root the selected assemblies. It's not recommended to root the entire framework.
To enable more aggressive removal of unreferenced code, set the `<TrimMode>` property to `link`.
To enable more aggressive removal of unreferenced code, set the `<TrimmerDefaultAction>` property to `link`.
To aid in troubleshooting some of the most common problems related to trimming add `<IlcGenerateCompleteTypeMetadata>true</IlcGenerateCompleteTypeMetadata>` to your project. This ensures types are preserved in their entirety, but the extra members that would otherwise be trimmed cannot be used in runtime reflection. This mode can turn some spurious `NullReferenceExceptions` (caused by reflection APIs returning a null) caused by trimming into more actionable exceptions.
The Native AOT compiler can remove unused metadata more effectively than non-Native deployment models. For example, it's possible to remove names and metadata for methods while keeping the native code of the method. The higher efficiency of trimming in Native AOT can result in differences in what's visible to reflection at runtime in trimming-unfriendly code. To increase compatibility with the less efficient non-Native trimming, set the `<IlcTrimMetadata>` property to `false`. This compatibility mode is not necessary if there are no trimming warnings.
## Options related to library features
Native AOT supports enabling and disabling all [documented framework library features](https://docs.microsoft.com/en-us/dotnet/core/deploying/trimming-options#trimming-framework-library-features). For example, to remove globalization specific code and data, add a `<InvariantGlobalization>true</InvariantGlobalization>` property to your project. Disabling a framework feature (or enabling a minimal mode of the feature) can result in significant size savings.
Since `PublishTrimmed` is implied to be true with Native AOT, some framework features such as binary serialization are disabled by default.
## Options related to metadata generation
* `<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).
* `<IlcTrimMetadata>true</IlcTrimMetadata>`: allows the compiler to remove reflection metadata from things that were not visible targets of reflection. By default, the compiler keeps metadata for everything that was compiled. With this option turned on, reflection metadata (and therefore reflection) will only be available for visible targets of reflection. Visible targets of reflection are things like assemblies rooted from the project file, RD.XML, ILLinkTrim descriptors, DynamicallyAccessedMembers annotations or DynamicDependency annotations.
## Options related to code generation
* `<IlcOptimizationPreference>Speed</IlcOptimizationPreference>`: when generating optimized code, favor code execution speed.
......
......@@ -597,6 +597,12 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet
if (systemTypeValue.RepresentedType.Type.IsDefType)
{
_reflectionMarker.Dependencies.Add(_factory.StructMarshallingData((DefType)systemTypeValue.RepresentedType.Type), "Marshal API");
if (intrinsicId == IntrinsicId.Marshal_PtrToStructure
&& systemTypeValue.RepresentedType.Type.GetParameterlessConstructor() is MethodDesc ctorMethod
&& !_factory.MetadataManager.IsReflectionBlocked(ctorMethod))
{
_reflectionMarker.Dependencies.Add(_factory.ReflectableMethod(ctorMethod), "Marshal API");
}
}
}
else
......
......@@ -77,6 +77,11 @@ protected override ISymbolNode GetBaseTypeNode(NodeFactory factory)
return _type.BaseType != null ? factory.NecessaryTypeSymbol(_type.BaseType.NormalizeInstantiation()) : null;
}
protected override ISymbolNode GetNonNullableValueTypeArrayElementTypeNode(NodeFactory factory)
{
return factory.ConstructedTypeSymbol(((ArrayType)_type).ElementType);
}
protected override int GCDescSize
{
get
......
......@@ -101,6 +101,11 @@ protected override ISymbolNode GetBaseTypeNode(NodeFactory factory)
return _type.BaseType != null ? factory.ConstructedTypeSymbol(_type.BaseType) : null;
}
protected override ISymbolNode GetNonNullableValueTypeArrayElementTypeNode(NodeFactory factory)
{
return factory.ConstructedTypeSymbol(((ArrayType)_type).ElementType);
}
protected override IEETypeNode GetInterfaceTypeNode(NodeFactory factory, TypeDesc interfaceType)
{
// The interface type will be visible to reflection and should be considered constructed.
......
......@@ -757,14 +757,29 @@ protected virtual ISymbolNode GetBaseTypeNode(NodeFactory factory)
return _type.BaseType != null ? factory.NecessaryTypeSymbol(_type.BaseType) : null;
}
protected virtual ISymbolNode GetNonNullableValueTypeArrayElementTypeNode(NodeFactory factory)
{
return factory.NecessaryTypeSymbol(((ArrayType)_type).ElementType);
}
private ISymbolNode GetRelatedTypeNode(NodeFactory factory)
{
ISymbolNode relatedTypeNode = null;
if (_type.IsArray || _type.IsPointer || _type.IsByRef)
if (_type.IsParameterizedType)
{
var parameterType = ((ParameterizedType)_type).ParameterType;
relatedTypeNode = factory.NecessaryTypeSymbol(parameterType);
if (_type.IsArray && parameterType.IsValueType && !parameterType.IsNullable)
{
// This might be a constructed type symbol. There are APIs on Array that allow allocating element
// types through runtime magic ("((Array)new NeverAllocated[1]).GetValue(0)" or IEnumerable) and we don't have
// visibility into that. Conservatively assume element types of constructed arrays are also constructed.
relatedTypeNode = GetNonNullableValueTypeArrayElementTypeNode(factory);
}
else
{
relatedTypeNode = factory.NecessaryTypeSymbol(parameterType);
}
}
else
{
......
......@@ -187,7 +187,7 @@ protected override MetadataCategory GetMetadataCategory(TypeDesc type)
return category;
}
protected override bool AllMethodsCanBeReflectable => (_generationOptions & UsageBasedMetadataGenerationOptions.ReflectedMembersOnly) == 0;
protected override bool AllMethodsCanBeReflectable => (_generationOptions & UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts) != 0;
protected override void ComputeMetadata(NodeFactory factory,
out byte[] metadataBlob,
......@@ -527,7 +527,7 @@ protected override void GetDependenciesDueToMethodCodePresenceInternal(ref Depen
}
// Presence of code might trigger the reflectability dependencies.
if ((_generationOptions & UsageBasedMetadataGenerationOptions.ReflectedMembersOnly) == 0)
if ((_generationOptions & UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts) != 0)
{
GetDependenciesDueToReflectability(ref dependencies, factory, method);
}
......@@ -538,7 +538,7 @@ public override void GetConditionalDependenciesDueToMethodCodePresence(ref Combi
MethodDesc typicalMethod = method.GetTypicalMethodDefinition();
// Ensure methods with genericness have the same reflectability by injecting a conditional dependency.
if ((_generationOptions & UsageBasedMetadataGenerationOptions.ReflectedMembersOnly) != 0
if ((_generationOptions & UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts) == 0
&& method != typicalMethod)
{
dependencies ??= new CombinedDependencyList();
......@@ -549,7 +549,7 @@ public override void GetConditionalDependenciesDueToMethodCodePresence(ref Combi
public override void GetDependenciesDueToVirtualMethodReflectability(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
{
if ((_generationOptions & UsageBasedMetadataGenerationOptions.ReflectedMembersOnly) == 0)
if ((_generationOptions & UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts) != 0)
{
// If we have a use of an abstract method, GetDependenciesDueToReflectability is not going to see the method
// as being used since there's no body. We inject a dependency on a new node that serves as a logical method body
......@@ -600,8 +600,40 @@ public override void GetDependenciesDueToAccess(ref DependencyList dependencies,
dependencies.Add(factory.DataflowAnalyzedMethod(methodIL.GetMethodILDefinition()), "Access to interesting field");
}
if ((_generationOptions & UsageBasedMetadataGenerationOptions.ReflectedMembersOnly) == 0
&& !IsReflectionBlocked(writtenField))
string reason = "Use of a field";
bool generatesMetadata = false;
if (!IsReflectionBlocked(writtenField))
{
if ((_generationOptions & UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts) != 0)
{
// If access to the field should trigger metadata generation, we should generate the field
generatesMetadata = true;
}
else
{
// There's an invalid suppression in the CoreLib that assumes used fields on attributes will be kept.
// It's used in the reflection-based implementation of Attribute.Equals and Attribute.GetHashCode.
// .NET Native used to have a non-reflection based implementation of Equals/GetHashCode to get around
// this problem. We could explore that as well, but for now, emulate the fact that accessed fields
// on custom attributes will be visible in reflection metadata.
MetadataType currentType = (MetadataType)writtenField.OwningType.BaseType;
while (currentType != null)
{
if (currentType.Module == factory.TypeSystemContext.SystemModule
&& currentType.Name == "Attribute" && currentType.Namespace == "System")
{
generatesMetadata = true;
reason = "Field of an attribute";
break;
}
currentType = currentType.MetadataBaseType;
}
}
}
if (generatesMetadata)
{
FieldDesc fieldToReport = writtenField;
......@@ -619,7 +651,7 @@ public override void GetDependenciesDueToAccess(ref DependencyList dependencies,
}
dependencies = dependencies ?? new DependencyList();
dependencies.Add(factory.ReflectableField(fieldToReport), "Use of a field");
dependencies.Add(factory.ReflectableField(fieldToReport), reason);
}
}
......@@ -1039,9 +1071,9 @@ public enum UsageBasedMetadataGenerationOptions
ReflectionILScanning = 4,
/// <summary>
/// Only members that were seen as reflected on will be reflectable.
/// Consider all native artifacts (native method bodies, etc) visible from reflection.
/// </summary>
ReflectedMembersOnly = 8,
CreateReflectableArtifacts = 8,
/// <summary>
/// Fully root used assemblies that are not marked IsTrimmable in metadata.
......
......@@ -53,9 +53,8 @@ internal class Program
private string _mapFileName;
private string _metadataLogFileName;
private bool _noMetadataBlocking;
private bool _disableReflection;
private string _reflectionData;
private bool _completeTypesMetadata;
private bool _reflectedOnly;
private bool _scanReflection;
private bool _methodBodyFolding;
private int _parallelism = Environment.ProcessorCount;
......@@ -159,6 +158,8 @@ private void InitializeDefaultOptions()
private ArgumentSyntax ParseCommandLine(string[] args)
{
var validReflectionDataOptions = new string[] { "all", "none" };
IReadOnlyList<string> inputFiles = Array.Empty<string>();
IReadOnlyList<string> referenceFiles = Array.Empty<string>();
......@@ -201,9 +202,8 @@ 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("reflectedonly", ref _reflectedOnly, "Generate metadata only for reflected members");
syntax.DefineOption("reflectiondata", ref _reflectionData, $"Reflection data to generate (one of: {string.Join(", ", validReflectionDataOptions)})");
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)");
syntax.DefineOption("noscan", ref _noScanner, "Do not use IL scanner to generate optimized code");
......@@ -342,6 +342,11 @@ private ArgumentSyntax ParseCommandLine(string[] args)
Helpers.MakeReproPackage(_makeReproPath, _outputFilePath, args, argSyntax, new[] { "-r", "-m", "--rdxml", "--directpinvokelist" });
}
if (_reflectionData != null && Array.IndexOf(validReflectionDataOptions, _reflectionData) < 0)
{
Console.WriteLine($"Warning: option '{_reflectionData}' not recognized");
}
return argSyntax;
}
......@@ -527,7 +532,7 @@ private int Run(string[] args)
InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture),
_targetArchitecture);
bool supportsReflection = !_disableReflection && _systemModuleName == DefaultSystemModule;
bool supportsReflection = _reflectionData != "none" && _systemModuleName == DefaultSystemModule;
//
// Initialize type system context
......@@ -758,8 +763,8 @@ static string ILLinkify(string rootedAssembly)
metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CompleteTypesOnly;
if (_scanReflection)
metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.ReflectionILScanning;
if (_reflectedOnly)
metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.ReflectedMembersOnly;
if (_reflectionData == "all")
metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts;
if (_rootDefaultAssemblies)
metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.RootDefaultAssemblies;
}
......
......@@ -150,5 +150,11 @@
</Method>
</Type>
</Assembly>
<Assembly Name="System.Private.CoreLib">
<!-- Test doing things the static analysis cannot predict. Could also fix the test. -->
<Type Name="System.Collections.Generic.Dictionary`2" Dynamic="Required All" />
<Type Name="System.Collections.Generic.HashSet`1" Dynamic="Required All" />
</Assembly>
</Application>
</Directives>
<Directives>
<Application>
<Assembly Name="System.Collections">
<Type Name="System.Collections.Generic.List`1" Dynamic="Required All" />
</Assembly>
<Assembly Name="System.Runtime">
<Type Name="System.Decimal" Dynamic="Required All" />
<Type Name="System.DateTimeOffset" Dynamic="Required All" />
<Type Name="System.TimeSpan" Dynamic="Required All" />
<Type Name="System.DateTime" Dynamic="Required All" />
<Type Name="System.Text.StringBuilder" Dynamic="Required All" />
<Type Name="System.Array" Dynamic="Required All" />
<Type Name="System.IEquatable`1[[System.Linq.Expressions.Tests.Number, System.Linq.Expressions.Tests]]">
<Method Name="Equals" Dynamic="Required All">
</Method>
......@@ -32,6 +42,13 @@
<Type Name="System.Action" Dynamic="Required All" />
<Type Name="System.Action`1[[System.Int32,System.Private.CoreLib]]" Dynamic="Required All" />
<Type Name="System.IEquatable`1[[System.Linq.Expressions.Tests.Scs, System.Linq.Expressions.Tests]]" Dynamic="Required All" />
<Type Name="System.IEquatable`1[[System.Linq.Expressions.Tests.Sp, System.Linq.Expressions.Tests]]" Dynamic="Required All" />
<Type Name="System.IEquatable`1[[System.Linq.Expressions.Tests.Ss, System.Linq.Expressions.Tests]]" Dynamic="Required All" />
<Type Name="System.IEquatable`1[[System.Linq.Expressions.Tests.S, System.Linq.Expressions.Tests]]" Dynamic="Required All" />
<Type Name="System.IEquatable`1[[System.Linq.Expressions.Tests.Sc, System.Linq.Expressions.Tests]]" Dynamic="Required All" />
</Assembly>
<Assembly Name="System.Linq">
<Type Name="System.Linq.Enumerable">
......@@ -45,6 +62,7 @@
<Type Name="System.Linq.Expressions.Expression`1[[System.Func`7[[System.Int32,System.Private.CoreLib],[System.Int32,System.Private.CoreLib],[System.Int32,System.Private.CoreLib],[System.Int32,System.Private.CoreLib],[System.Int32,System.Private.CoreLib],[System.Int32,System.Private.CoreLib],[System.Int32,System.Private.CoreLib]],System.Private.CoreLib]]" Dynamic="Required All" />
<Type Name="System.Linq.Expressions.Expression`1[[System.Func`3[[System.Nullable`1[[System.Boolean,System.Private.CoreLib]],System.Private.CoreLib],[System.Nullable`1[[System.Boolean,System.Private.CoreLib]],System.Private.CoreLib],[System.Boolean,System.Private.CoreLib]],System.Private.CoreLib]]" Dynamic="Required All" />
<Type Name="System.Runtime.CompilerServices.CallSite`1" Dynamic="Required All" />
<Type Name="System.Runtime.CompilerServices.IRuntimeVariables" Dynamic="Required All" />
</Assembly>
<Assembly Name="System.Linq.Expressions.Tests">
<Type Name="System.Linq.Expressions.Tests.OpAssign+Box`1[[System.Double,System.Private.CoreLib]]" Dynamic="Required All" />
......
......@@ -47,5 +47,9 @@
<Type Name="System.Reflection.Tests.MemberInfoTests+GenericTestClass`1[[System.Double, System.Private.CoreLib]]" Dynamic="Required All" />
</Assembly>
<Assembly Name="System.Private.CoreLib">
<Type Name="System.Collections.Generic.Dictionary`2" Dynamic="Required All" />
</Assembly>
</Application>
</Directives>
......@@ -501,6 +501,7 @@
</Type>
<Type Name="System.Int128" Dynamic="Required All" />
<Type Name="System.Half" Dynamic="Required All" />
<Type Name="System.Collections.Generic.NonRandomizedStringEqualityComparer" Dynamic="Required All" />
</Assembly>
</Application>
</Directives>
......@@ -389,7 +389,10 @@ public static void Run()
Assert.Equal(1, typeof(TypeWithSpecificMethodKept).CountMethods());
Assert.Equal(1, typeof(TypeWithSpecificOverloadKept).CountMethods());
Assert.Equal(2, typeof(TypeWithAllOverloadsKept).CountMethods());
Assert.Equal(2, typeof(TestDynamicDependency).CountMethods());
// We only expect DependentMethod. We specifically don't expect to see the Run method (current method).
Assert.Equal(1, typeof(TestDynamicDependency).CountMethods());
Assert.Equal(1, typeof(TypeWithPublicPropertiesKept).CountProperties());
}
}
......
......@@ -17,6 +17,7 @@ static int Main()
TestAbstractNeverDerivedWithDevirtualizedCall.Run();
TestAbstractDerivedByUnrelatedTypeWithDevirtualizedCall.Run();
TestUnusedDefaultInterfaceMethod.Run();
TestArrayElementTypeOperations.Run();
return 100;
}
......@@ -219,6 +220,60 @@ public static void Run()
}
}
class TestArrayElementTypeOperations
{
public static void Run()
{
Console.WriteLine("Testing array element type optimizations");
// We consider valuetype elements of arrays constructed...
{
Array arr = new NeverAllocated1[1];
ThrowIfNotPresent(typeof(TestArrayElementTypeOperations), nameof(Marker1));
// The reason they're considered constructed is runtime magic here
// Make sure that works too.
object o = arr.GetValue(0);
if (!o.ToString().Contains(nameof(Marker1)))
throw new Exception();
}
// ...but not nullable...
{
Array arr = new Nullable<NeverAllocated2>[1];
arr.GetValue(0);
ThrowIfPresent(typeof(TestArrayElementTypeOperations), nameof(Marker2));
}
// ...or reference type element types
{
Array arr = new NeverAllocated3[1];
arr.GetValue(0);
ThrowIfPresent(typeof(TestArrayElementTypeOperations), nameof(Marker3));
}
}
class Marker1 { }
struct NeverAllocated1
{
public override string ToString() => typeof(Marker1).ToString();
}
class Marker2 { }
struct NeverAllocated2
{
public override string ToString() => typeof(Marker2).ToString();
}
class Marker3 { }
class NeverAllocated3
{
public override string ToString() => typeof(Marker3).ToString();
}
}
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "That's the point")]
private static bool IsTypePresent(Type testType, string typeName) => testType.GetNestedType(typeName, BindingFlags.NonPublic | BindingFlags.Public) != null;
......@@ -230,4 +285,12 @@ private static void ThrowIfPresent(Type testType, string typeName)
throw new Exception(typeName);
}
}
private static void ThrowIfNotPresent(Type testType, string typeName)
{
if (!IsTypePresent(testType, typeName))
{
throw new Exception(typeName);
}
}
}
......@@ -113,6 +113,27 @@
</Method>
</Type>
<Type Name="CtorDict.MyType1">
<Method Name=".ctor" Dynamic="Required All" />
</Type>
<Type Name="CtorDict.MyType2">
<Method Name=".ctor" Dynamic="Required All" />
</Type>
<Type Name="CtorDict.MyType5">
<Method Name=".ctor" Dynamic="Required All" />
</Type>
<Type Name="CtorDict.MyType6">
<Method Name=".ctor" Dynamic="Required All" />
</Type>
<Type Name="ConstraintsTests+TypeWithPublicCtor">
<Method Name=".ctor" Dynamic="Required All" />
</Type>
<Type Name="MethodConstraintsTests+TypeWithPublicCtor">
<Method Name=".ctor" Dynamic="Required All" />
</Type>
<Type Name="MethodAndUnboxingStubTesting.GenericClass`2[[System.Object, netstandard],[System.Object, netstandard]]" Dynamic="Required All"/>
<Type Name="MethodAndUnboxingStubTesting.GenericClass2`2[[System.Object, netstandard],[System.Object, netstandard]]" Dynamic="Required All"/>
<Type Name="MethodAndUnboxingStubTesting.GenericStruct`2[[System.Object, netstandard],[System.Object, netstandard]]" Dynamic="Required All"/>
......
......@@ -5,6 +5,12 @@
<CLRTestPriority>0</CLRTestPriority>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CLRTestTargetUnsupported Condition="'$(IlcMultiModule)' == 'true'">true</CLRTestTargetUnsupported>
<!--
We rely on reflection metadata for .cctor to detect if a type was preinitialized
and this only works if presence of code implies presence of reflection metadata.
-->
<IlcTrimMetadata>false</IlcTrimMetadata>
</PropertyGroup>
<ItemGroup>
......
......@@ -55,6 +55,10 @@ private static int Main()
TestNotReflectedIsNotReflectable.Run();
TestGenericInstantiationsAreEquallyReflectable.Run();
#endif
TestAttributeInheritance2.Run();
TestInvokeMethodMetadata.Run();
TestVTableOfNullableUnderlyingTypes.Run();
TestInterfaceLists.Run();
//
// Mostly functionality tests
......@@ -1875,6 +1879,97 @@ public static void Run()
}
}
class TestAttributeInheritance2
{
[AttributeUsage(AttributeTargets.All, Inherited = true)]
class AttAttribute : Attribute { }
class Base
{
[Att]
public virtual void VirtualMethodWithAttribute() { }
}
class Derived : Base
{
public override void VirtualMethodWithAttribute() { }
}
public static void Run()
{
object[] attrs = typeof(Derived).GetMethod(nameof(Derived.VirtualMethodWithAttribute)).GetCustomAttributes(inherit: true);
if (attrs.Length != 1 || attrs[0].GetType().Name != nameof(AttAttribute))
{
throw new Exception();
}
}
}
class TestInvokeMethodMetadata
{
delegate int WithDefaultParameter1(int value = 1234);
delegate DateTime WithDefaultParameter2(DateTime value);
public static int Method(int value) => value;
public static DateTime Method(DateTime value) => value;
public static void Run()
{
// Check that we have metadata for the Invoke method to convert Type.Missing to the actual value.
WithDefaultParameter1 del1 = Method;
int val = (int)del1.DynamicInvoke(new object[] { Type.Missing });
if (val != 1234)
throw new Exception();
// Check that we have metadata for the Invoke method to find a matching method
Delegate del2 = Delegate.CreateDelegate(typeof(WithDefaultParameter2), typeof(TestInvokeMethodMetadata), nameof(Method));
if (del2.Method.ReturnType != typeof(DateTime))
throw new Exception();
}
}
class TestVTableOfNullableUnderlyingTypes
{
struct NeverAllocated
{
public override string ToString() => "Never allocated";
}
static Type s_hidden = typeof(Nullable<NeverAllocated>);
public static void Run()
{
// Underlying type of a Nullable needs to be considered constructed.
// Trimming warning suppressions in the libraries depend on this invariant.
var instance = RuntimeHelpers.GetUninitializedObject(s_hidden);
if (instance.ToString() != "Never allocated")
throw new Exception();
}
}
class TestInterfaceLists
{
interface IGeneric<T> { }
class Class<T> : IGeneric<T> { }
static Type s_hidden = typeof(Class<string>);
public static void Run()
{
// Can't drop an interface from the interface list if the interface is referenced.
// Trimming warning suppressions in the libraries depend on this invariant.
foreach (var intface in s_hidden.GetInterfaces())
{
if (intface.HasSameMetadataDefinitionAs(typeof(IGeneric<object>)))
return;
}
throw new Exception();
}
}
#region Helpers
private static Type SecretGetType(string testName, string typeName)
......
......@@ -4,7 +4,6 @@
<CLRTestKind>BuildAndRun</CLRTestKind>
<CLRTestPriority>0</CLRTestPriority>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>$(DefineConstants);REFLECTION_FROM_USAGE</DefineConstants>
<!-- There's just too many of these warnings -->
<SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
......
......@@ -4,6 +4,7 @@
<CLRTestKind>BuildAndRun</CLRTestKind>
<CLRTestPriority>0</CLRTestPriority>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>$(DefineConstants);REFLECTION_FROM_USAGE</DefineConstants>
<!-- There's just too many of these warnings -->
<SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
......@@ -11,12 +12,10 @@
<!-- Look for MULTIMODULE_BUILD #define for the more specific incompatible parts -->
<CLRTestTargetUnsupported Condition="'$(IlcMultiModule)' == 'true'">true</CLRTestTargetUnsupported>
<IlcTrimMetadata>false</IlcTrimMetadata>
</PropertyGroup>
<ItemGroup>
<Compile Include="Reflection.cs" />
</ItemGroup>
<ItemGroup>
<IlcArg Include="--reflectedonly" />
</ItemGroup>
</Project>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册