diff --git a/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs b/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs index df62b988f20e1..31ae384844d76 100644 --- a/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs +++ b/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs @@ -89,7 +89,10 @@ public static string GetDisplayName(this MethodDesc method) if (method.Signature.Length > 0) { for (int i = 0; i < method.Signature.Length - 1; i++) - sb.Append(method.Signature[i].GetDisplayNameWithoutNamespace()).Append(','); + { + TypeDesc instantiatedType = method.Signature[i].InstantiateSignature(method.OwningType.Instantiation, method.Instantiation); + sb.Append(instantiatedType.GetDisplayNameWithoutNamespace()).Append(','); + } sb.Append(method.Signature[method.Signature.Length - 1].GetDisplayNameWithoutNamespace()); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs index e724d905a5053..5804b1b036919 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs @@ -46,8 +46,8 @@ public bool RequiresDataflowAnalysis(MethodDesc method) try { method = method.GetTypicalMethodDefinition(); - return GetAnnotations(method.OwningType).TryGetAnnotation(method, out var methodAnnotations) - && (methodAnnotations.ReturnParameterAnnotation != DynamicallyAccessedMemberTypes.None || methodAnnotations.ParameterAnnotations != null); + TypeAnnotations typeAnnotations = GetAnnotations(method.OwningType); + return typeAnnotations.HasGenericParameterAnnotation() || typeAnnotations.TryGetAnnotation(method, out _); } catch (TypeSystemException) { @@ -73,7 +73,8 @@ public bool RequiresDataflowAnalysis(FieldDesc field) try { field = field.GetTypicalFieldDefinition(); - return GetAnnotations(field.OwningType).TryGetAnnotation(field, out _); + TypeAnnotations typeAnnotations = GetAnnotations(field.OwningType); + return typeAnnotations.HasGenericParameterAnnotation() || typeAnnotations.TryGetAnnotation(field, out _); } catch (TypeSystemException) { @@ -105,6 +106,31 @@ public bool HasAnyAnnotations(TypeDesc type) } } + public bool HasGenericParameterAnnotation(TypeDesc type) + { + try + { + return GetAnnotations(type.GetTypeDefinition()).HasGenericParameterAnnotation(); + } + catch (TypeSystemException) + { + return false; + } + } + + public bool HasGenericParameterAnnotation(MethodDesc method) + { + try + { + method = method.GetTypicalMethodDefinition(); + return GetAnnotations(method.OwningType).TryGetAnnotation(method, out var annotation) && annotation.GenericParameterAnnotations != null; + } + catch (TypeSystemException) + { + return false; + } + } + internal DynamicallyAccessedMemberTypes GetParameterAnnotation(ParameterProxy param) { MethodDesc method = param.Method.Method.GetTypicalMethodDefinition(); @@ -884,6 +910,8 @@ public bool TryGetAnnotation(GenericParameterDesc genericParameter, out Dynamica return false; } + + public bool HasGenericParameterAnnotation() => _genericParameterAnnotations != null; } private readonly struct MethodAnnotations diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs index abd667654e911..7ac0815a77c74 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs @@ -18,45 +18,140 @@ namespace ILCompiler.Dataflow { - internal readonly struct GenericArgumentDataFlow + internal static class GenericArgumentDataFlow { - private readonly Logger _logger; - private readonly NodeFactory _factory; - private readonly FlowAnnotations _annotations; - private readonly MessageOrigin _origin; + public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, NodeFactory factory, in MessageOrigin origin, TypeDesc type, TypeDesc contextType) + { + ProcessGenericArgumentDataFlow(ref dependencies, factory, origin, type, contextType.Instantiation, Instantiation.Empty); + } - public GenericArgumentDataFlow(Logger logger, NodeFactory factory, FlowAnnotations annotations, in MessageOrigin origin) + public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, NodeFactory factory, in MessageOrigin origin, TypeDesc type, MethodDesc contextMethod) { - _logger = logger; - _factory = factory; - _annotations = annotations; - _origin = origin; + ProcessGenericArgumentDataFlow(ref dependencies, factory, origin, type, contextMethod.OwningType.Instantiation, contextMethod.Instantiation); } - public DependencyList ProcessGenericArgumentDataFlow(GenericParameterDesc genericParameter, TypeDesc genericArgument) + private static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, NodeFactory factory, in MessageOrigin origin, TypeDesc type, Instantiation typeContext, Instantiation methodContext) { - var genericParameterValue = _annotations.GetGenericParameterValue(genericParameter); - Debug.Assert(genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None); + if (!type.HasInstantiation) + return; - MultiValue genericArgumentValue = _annotations.GetTypeValueFromGenericArgument(genericArgument); + TypeDesc instantiatedType = type.InstantiateSignature(typeContext, methodContext); + + var mdManager = (UsageBasedMetadataManager)factory.MetadataManager; var diagnosticContext = new DiagnosticContext( - _origin, - _logger.ShouldSuppressAnalysisWarningsForRequires(_origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute), - _logger); - return RequireDynamicallyAccessedMembers(diagnosticContext, genericArgumentValue, genericParameterValue, genericParameter.GetDisplayName()); - } - - private DependencyList RequireDynamicallyAccessedMembers( - in DiagnosticContext diagnosticContext, - in MultiValue value, - ValueWithDynamicallyAccessedMembers targetValue, - string reason) - { - var reflectionMarker = new ReflectionMarker(_logger, _factory, _annotations, typeHierarchyDataFlowOrigin: null, enabled: true); - var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(reflectionMarker, diagnosticContext, reason); - requireDynamicallyAccessedMembersAction.Invoke(value, targetValue); - return reflectionMarker.Dependencies; + origin, + !mdManager.Logger.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute), + mdManager.Logger); + var reflectionMarker = new ReflectionMarker(mdManager.Logger, factory, mdManager.FlowAnnotations, typeHierarchyDataFlowOrigin: null, enabled: true); + + ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, instantiatedType); + + if (reflectionMarker.Dependencies.Count > 0) + { + if (dependencies == null) + dependencies = reflectionMarker.Dependencies; + else + dependencies.AddRange(reflectionMarker.Dependencies); + } + } + + public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, TypeDesc type) + { + TypeDesc typeDefinition = type.GetTypeDefinition(); + if (typeDefinition != type) + { + ProcessGenericInstantiation(diagnosticContext, reflectionMarker, type.Instantiation, typeDefinition.Instantiation); + } + } + + public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, MethodDesc method) + { + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); + if (typicalMethod != method) + { + ProcessGenericInstantiation(diagnosticContext, reflectionMarker, method.Instantiation, typicalMethod.Instantiation); + } + + ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, method.OwningType); + } + + public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, FieldDesc field) + { + ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, field.OwningType); + } + + private static void ProcessGenericInstantiation(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, Instantiation instantiation, Instantiation typicalInstantiation) + { + for (int i = 0; i < instantiation.Length; i++) + { + // Apply annotations to the generic argument + var genericArgument = instantiation[i]; + var genericParameter = (GenericParameterDesc)typicalInstantiation[i]; + if (reflectionMarker.Annotations.GetGenericParameterAnnotation(genericParameter) != default) + { + var genericParameterValue = reflectionMarker.Annotations.GetGenericParameterValue(genericParameter); + Debug.Assert(genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None); + MultiValue genericArgumentValue = reflectionMarker.Annotations.GetTypeValueFromGenericArgument(genericArgument); + var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(reflectionMarker, diagnosticContext, genericParameter.GetDisplayName()); + requireDynamicallyAccessedMembersAction.Invoke(genericArgumentValue, genericParameterValue); + } + + // Recursively process generic argument data flow on the generic argument if it itself is generic + if (genericArgument.HasInstantiation) + { + ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, genericArgument); + } + } + } + + public static bool RequiresGenericArgumentDataFlow(FlowAnnotations flowAnnotations, MethodDesc method) + { + // Method callsites can contain generic instantiations which may contain annotations inside nested generics + // so we have to check all of the instantiations for that case. + // For example: + // OuterGeneric>.Method>(); + + if (method.HasInstantiation) + { + if (flowAnnotations.HasGenericParameterAnnotation(method)) + return true; + + foreach (TypeDesc typeParameter in method.Instantiation) + { + if (RequiresGenericArgumentDataFlow(flowAnnotations, typeParameter)) + return true; + } + } + + return RequiresGenericArgumentDataFlow(flowAnnotations, method.OwningType); + } + + public static bool RequiresGenericArgumentDataFlow(FlowAnnotations flowAnnotations, FieldDesc field) + // Field access can contain generic instantiations which may contain annotations inside nested generics + // For example: + // OuterGeneric>.Field + => RequiresGenericArgumentDataFlow(flowAnnotations, field.OwningType); + + /// + /// For a given type determines if its usage means we need to run the callsite through data flow. + /// This is purely for type references alone, so for example for generic parameters. + /// + public static bool RequiresGenericArgumentDataFlow(FlowAnnotations flowAnnotations, TypeDesc type) + { + if (flowAnnotations.HasGenericParameterAnnotation(type)) + return true; + + if (type.HasInstantiation) + { + foreach (TypeDesc typeParameter in type.Instantiation) + { + if (RequiresGenericArgumentDataFlow(flowAnnotations, typeParameter)) + return true; + } + } + + return false; } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs index b9d09dbc87fbe..fa83eca4f903c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs @@ -800,7 +800,7 @@ protected virtual void Scan(MethodIL methodBody, ref InterproceduralState interp StackSlot retValue = PopUnknown(currentStack, 1, methodBody, offset); // If the return value is a reference, treat it as the value itself for now // We can handle ref return values better later - ReturnValue = MultiValueLattice.Meet(ReturnValue, DereferenceValue(retValue.Value, locals, ref interproceduralState)); + ReturnValue = MultiValueLattice.Meet(ReturnValue, DereferenceValue(methodBody, offset, retValue.Value, locals, ref interproceduralState)); ValidateNoReferenceToReference(locals, methodBody, offset); } ClearStack(ref currentStack); @@ -947,23 +947,24 @@ private void ScanLdtoken(MethodIL methodBody, int offset, object operand, Stack< var nullableDam = new RuntimeTypeHandleForNullableValueWithDynamicallyAccessedMembers(new TypeProxy(type), new RuntimeTypeHandleForGenericParameterValue(genericParam)); currentStack.Push(new StackSlot(nullableDam)); - return; + break; case MetadataType underlyingType: var nullableType = new RuntimeTypeHandleForNullableSystemTypeValue(new TypeProxy(type), new SystemTypeValue(underlyingType)); currentStack.Push(new StackSlot(nullableType)); - return; + break; default: PushUnknown(currentStack); - return; + break; } } else { var typeHandle = new RuntimeTypeHandleValue(new TypeProxy(type)); currentStack.Push(new StackSlot(typeHandle)); - return; } } + + HandleTypeReflectionAccess(methodBody, offset, type); } else if (operand is MethodDesc method) { @@ -1026,7 +1027,7 @@ protected void StoreInReference(MultiValue target, MultiValue source, MethodIL m StoreMethodLocalValue(locals, source, localReference.LocalIndex, curBasicBlock); break; case FieldReferenceValue fieldReference - when GetFieldValue(fieldReference.FieldDefinition).AsSingleValue() is FieldValue fieldValue: + when HandleGetField(method, offset, fieldReference.FieldDefinition).AsSingleValue() is FieldValue fieldValue: HandleStoreField(method, offset, fieldValue, source); break; case ParameterReferenceValue parameterReference @@ -1038,7 +1039,7 @@ when GetMethodParameterValue(parameterReference.Parameter) is MethodParameterVal HandleStoreMethodReturnValue(method, offset, methodReturnValue, source); break; case FieldValue fieldValue: - HandleStoreField(method, offset, fieldValue, DereferenceValue(source, locals, ref ipState)); + HandleStoreField(method, offset, fieldValue, DereferenceValue(method, offset, source, locals, ref ipState)); break; case IValueWithStaticType valueWithStaticType: if (valueWithStaticType.StaticType is not null && FlowAnnotations.IsTypeInterestingForDataflow(valueWithStaticType.StaticType)) @@ -1057,7 +1058,25 @@ when GetMethodParameterValue(parameterReference.Parameter) is MethodParameterVal } - protected abstract MultiValue GetFieldValue(FieldDesc field); + /// + /// HandleGetField is called every time the scanner needs to represent a value of the field + /// either as a source or target. It is not called when just a reference to field is created, + /// But if such reference is dereferenced then it will get called. + /// It is NOT called for hoisted locals. + /// + /// + /// There should be no need to perform checks for hoisted locals. All of our reflection checks are based + /// on an assumption that problematic things happen because of running code. Doing things purely in the type system + /// (declaring new types which are never instantiated, declaring fields which are never assigned to, ...) + /// don't cause problems (or better way, they won't show observable behavioral differences). + /// Typically that would mean that accessing fields is also an uninteresting operation, unfortunately + /// static fields access can cause execution of static .cctor and that is running code -> possible problems. + /// So we have to track accesses in that case. + /// Hoisted locals are fields on closure classes/structs which should not have static .ctors, so we don't + /// need to track those. It makes the design a bit cleaner because hoisted locals are purely handled in here + /// and don't leak over to the reflection handling code in any way. + /// + protected abstract MultiValue HandleGetField(MethodIL methodBody, int offset, FieldDesc field); private void ScanLdfld( MethodIL methodBody, @@ -1083,7 +1102,7 @@ private void ScanLdfld( } else { - value = GetFieldValue(field); + value = HandleGetField(methodBody, offset, field); } currentStack.Push(new StackSlot(value)); } @@ -1119,7 +1138,7 @@ private void ScanStfld( return; } - foreach (var value in GetFieldValue(field)) + foreach (var value in HandleGetField(methodBody, offset, field)) { // GetFieldValue may return different node types, in which case they can't be stored to. // At least not yet. @@ -1127,7 +1146,7 @@ private void ScanStfld( continue; // Incomplete handling of ref fields -- if we're storing a reference to a value, pretend it's just the value - MultiValue valueToStore = DereferenceValue(valueToStoreSlot.Value, locals, ref interproceduralState); + MultiValue valueToStore = DereferenceValue(methodBody, offset, valueToStoreSlot.Value, locals, ref interproceduralState); HandleStoreField(methodBody, offset, fieldValue, valueToStore); } @@ -1163,7 +1182,12 @@ private ValueNodeList PopCallArguments( return methodParams; } - internal MultiValue DereferenceValue(MultiValue maybeReferenceValue, ValueBasicBlockPair?[] locals, ref InterproceduralState interproceduralState) + internal MultiValue DereferenceValue( + MethodIL methodBody, + int offset, + MultiValue maybeReferenceValue, + ValueBasicBlockPair?[] locals, + ref InterproceduralState interproceduralState) { MultiValue dereferencedValue = MultiValueLattice.Top; foreach (var value in maybeReferenceValue) @@ -1175,7 +1199,7 @@ internal MultiValue DereferenceValue(MultiValue maybeReferenceValue, ValueBasicB dereferencedValue, CompilerGeneratedState.IsHoistedLocal(fieldReferenceValue.FieldDefinition) ? interproceduralState.GetHoistedLocal(new HoistedLocalKey(fieldReferenceValue.FieldDefinition)) - : GetFieldValue(fieldReferenceValue.FieldDefinition)); + : HandleGetField(methodBody, offset, fieldReferenceValue.FieldDefinition)); break; case ParameterReferenceValue parameterReferenceValue: dereferencedValue = MultiValue.Meet( @@ -1224,6 +1248,11 @@ protected void AssignRefAndOutParameters( } } + /// + /// Called when type is accessed directly (basically only ldtoken) + /// + protected abstract void HandleTypeReflectionAccess(MethodIL methodBody, int offset, TypeDesc accessedType); + /// /// Called to handle reflection access to a method without any other specifics (ldtoken or ldftn for example) /// @@ -1260,7 +1289,7 @@ private void HandleCall( var dereferencedMethodParams = new List(); foreach (var argument in methodArguments) - dereferencedMethodParams.Add(DereferenceValue(argument, locals, ref interproceduralState)); + dereferencedMethodParams.Add(DereferenceValue(callingMethodBody, offset, argument, locals, ref interproceduralState)); MultiValue methodReturnValue; bool handledFunction = HandleCall( callingMethodBody, diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs index 7a73ba40d0812..c687ee5867dbc 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -30,13 +30,14 @@ internal sealed class ReflectionMethodBodyScanner : MethodBodyScanner private MessageOrigin _origin; - public static bool RequiresReflectionMethodBodyScannerForCallSite(FlowAnnotations flowAnnotations, MethodDesc methodDefinition) + public static bool RequiresReflectionMethodBodyScannerForCallSite(FlowAnnotations flowAnnotations, MethodDesc method) { - return Intrinsics.GetIntrinsicIdForMethod(methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || - flowAnnotations.RequiresDataflowAnalysis(methodDefinition) || - methodDefinition.DoesMethodRequire(DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _) || - methodDefinition.DoesMethodRequire(DiagnosticUtilities.RequiresDynamicCodeAttribute, out _) || - IsPInvokeDangerous(methodDefinition, out _, out _); + return Intrinsics.GetIntrinsicIdForMethod(method) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || + flowAnnotations.RequiresDataflowAnalysis(method) || + GenericArgumentDataFlow.RequiresGenericArgumentDataFlow(flowAnnotations, method) || + method.DoesMethodRequire(DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _) || + method.DoesMethodRequire(DiagnosticUtilities.RequiresDynamicCodeAttribute, out _) || + IsPInvokeDangerous(method, out _, out _); } public static bool RequiresReflectionMethodBodyScannerForMethodBody(FlowAnnotations flowAnnotations, MethodDesc methodDefinition) @@ -45,11 +46,12 @@ public static bool RequiresReflectionMethodBodyScannerForMethodBody(FlowAnnotati flowAnnotations.RequiresDataflowAnalysis(methodDefinition); } - public static bool RequiresReflectionMethodBodyScannerForAccess(FlowAnnotations flowAnnotations, FieldDesc fieldDefinition) + public static bool RequiresReflectionMethodBodyScannerForAccess(FlowAnnotations flowAnnotations, FieldDesc field) { - return flowAnnotations.RequiresDataflowAnalysis(fieldDefinition) || - fieldDefinition.DoesFieldRequire(DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _) || - fieldDefinition.DoesFieldRequire(DiagnosticUtilities.RequiresDynamicCodeAttribute, out _); + return flowAnnotations.RequiresDataflowAnalysis(field) || + GenericArgumentDataFlow.RequiresGenericArgumentDataFlow(flowAnnotations, field) || + field.DoesFieldRequire(DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _) || + field.DoesFieldRequire(DiagnosticUtilities.RequiresDynamicCodeAttribute, out _); } internal static void CheckAndReportRequires(in DiagnosticContext diagnosticContext, TypeSystemEntity calledMember, string requiresAttributeName) @@ -139,7 +141,19 @@ protected override ValueWithDynamicallyAccessedMembers GetMethodParameterValue(P private MethodParameterValue GetMethodParameterValue(ParameterProxy parameter, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) => _annotations.GetMethodParameterValue(parameter, dynamicallyAccessedMemberTypes); - protected override MultiValue GetFieldValue(FieldDesc field) => _annotations.GetFieldValue(field); + /// + /// HandleGetField is called every time the scanner needs to represent a value of the field + /// either as a source or target. It is not called when just a reference to field is created, + /// But if such reference is dereferenced then it will get called. + /// + protected override MultiValue HandleGetField(MethodIL methodBody, int offset, FieldDesc field) + { + _origin = _origin.WithInstructionOffset(methodBody, offset); + + ProcessGenericArgumentDataFlow(field); + + return _annotations.GetFieldValue(field); + } private void HandleStoreValueWithDynamicallyAccessedMembers(MethodIL methodBody, int offset, ValueWithDynamicallyAccessedMembers targetValue, MultiValue sourceValue, string reason) { @@ -160,16 +174,33 @@ protected override void HandleStoreParameter(MethodIL methodBody, int offset, Me protected override void HandleStoreMethodReturnValue(MethodIL methodBody, int offset, MethodReturnValue returnValue, MultiValue valueToStore) => HandleStoreValueWithDynamicallyAccessedMembers(methodBody, offset, returnValue, valueToStore, returnValue.Method.GetDisplayName()); + protected override void HandleTypeReflectionAccess(MethodIL methodBody, int offset, TypeDesc accessedType) + { + // Note that ldtoken alone is technically a reflection access to the type + // it doesn't lead to full reflection marking of the type + // since we implement full dataflow for type values and accesses to them. + _origin = _origin.WithInstructionOffset(methodBody, offset); + + // Only check for generic instantiations. + ProcessGenericArgumentDataFlow(accessedType); + } + protected override void HandleMethodReflectionAccess(MethodIL methodBody, int offset, MethodDesc accessedMethod) { _origin = _origin.WithInstructionOffset(methodBody, offset); + TrimAnalysisPatterns.Add(new TrimAnalysisReflectionAccessPattern(accessedMethod, _origin)); + + ProcessGenericArgumentDataFlow(accessedMethod); } protected override void HandleFieldReflectionAccess(MethodIL methodBody, int offset, FieldDesc accessedField) { _origin = _origin.WithInstructionOffset(methodBody, offset); + TrimAnalysisPatterns.Add(new TrimAnalysisReflectionAccessPattern(accessedField, _origin)); + + ProcessGenericArgumentDataFlow(accessedField); } public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMethod, ILOpcode operation, int offset, ValueNodeList methodParams, out MultiValue methodReturnValue) @@ -201,6 +232,8 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet _origin )); + ProcessGenericArgumentDataFlow(calledMethod); + var diagnosticContext = new DiagnosticContext(_origin, diagnosticsEnabled: false, _logger); return HandleCall( callingMethodBody, @@ -634,6 +667,41 @@ private void HandleAssignmentPattern( TrimAnalysisPatterns.Add(new TrimAnalysisAssignmentPattern(value, targetValue, origin, reason)); } + private void ProcessGenericArgumentDataFlow(MethodDesc method) + { + // We only need to validate static methods and then all generic methods + // Instance non-generic methods don't need validation because the creation of the instance + // is the place where the validation will happen. + if (!method.Signature.IsStatic && !method.HasInstantiation && !method.IsConstructor) + return; + + if (GenericArgumentDataFlow.RequiresGenericArgumentDataFlow(_annotations, method)) + { + TrimAnalysisPatterns.Add(new TrimAnalysisGenericInstantiationAccessPattern(method, _origin)); + } + } + + private void ProcessGenericArgumentDataFlow(FieldDesc field) + { + // We only need to validate static field accesses, instance field accesses don't need generic parameter validation + // because the create of the instance would do that instead. + if (!field.IsStatic) + return; + + if (GenericArgumentDataFlow.RequiresGenericArgumentDataFlow(_annotations, field)) + { + TrimAnalysisPatterns.Add(new TrimAnalysisGenericInstantiationAccessPattern(field, _origin)); + } + } + + private void ProcessGenericArgumentDataFlow(TypeDesc type) + { + if (type.HasInstantiation && _annotations.HasGenericParameterAnnotation(type)) + { + TrimAnalysisPatterns.Add(new TrimAnalysisGenericInstantiationAccessPattern(type, _origin)); + } + } + private static bool IsPInvokeDangerous(MethodDesc calledMethod, out bool comDangerousMethod, out bool aotUnsafeDelegate) { if (!calledMethod.IsPInvoke) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisGenericInstantiationAccessPattern.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisGenericInstantiationAccessPattern.cs new file mode 100644 index 0000000000000..a9a7bbb9b044b --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisGenericInstantiationAccessPattern.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler.Logging; +using ILLink.Shared.TrimAnalysis; +using Internal.TypeSystem; + +#nullable enable + +namespace ILCompiler.Dataflow +{ + public readonly record struct TrimAnalysisGenericInstantiationAccessPattern + { + public TypeSystemEntity Entity { init; get; } + public MessageOrigin Origin { init; get; } + + internal TrimAnalysisGenericInstantiationAccessPattern(TypeSystemEntity entity, MessageOrigin origin) + { + Entity = entity; + Origin = origin; + } + + // No Merge - there's nothing to merge since this pattern is uniquely identified by both the origin and the entity + // and there's only one way to "access" a generic instantiation. + + public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker, Logger logger) + { + var diagnosticContext = new DiagnosticContext( + Origin, + logger.ShouldSuppressAnalysisWarningsForRequires(Origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute), + logger.ShouldSuppressAnalysisWarningsForRequires(Origin.MemberDefinition, DiagnosticUtilities.RequiresDynamicCodeAttribute), + logger.ShouldSuppressAnalysisWarningsForRequires(Origin.MemberDefinition, DiagnosticUtilities.RequiresAssemblyFilesAttribute), + logger); + + switch (Entity) + { + case TypeDesc type: + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, type); + break; + + case MethodDesc method: + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, method); + break; + + case FieldDesc field: + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, field); + break; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisPatternStore.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisPatternStore.cs index 85c9aa72eee38..a241f386489d1 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisPatternStore.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisPatternStore.cs @@ -16,6 +16,7 @@ public readonly struct TrimAnalysisPatternStore private readonly Dictionary<(MessageOrigin, bool), TrimAnalysisAssignmentPattern> AssignmentPatterns; private readonly Dictionary MethodCallPatterns; private readonly Dictionary<(MessageOrigin, TypeSystemEntity), TrimAnalysisReflectionAccessPattern> ReflectionAccessPatterns; + private readonly Dictionary<(MessageOrigin, TypeSystemEntity), TrimAnalysisGenericInstantiationAccessPattern> GenericInstantiations; private readonly ValueSetLattice Lattice; private readonly Logger _logger; @@ -24,6 +25,7 @@ public TrimAnalysisPatternStore(ValueSetLattice lattice, Logger log AssignmentPatterns = new Dictionary<(MessageOrigin, bool), TrimAnalysisAssignmentPattern>(); MethodCallPatterns = new Dictionary(); ReflectionAccessPatterns = new Dictionary<(MessageOrigin, TypeSystemEntity), TrimAnalysisReflectionAccessPattern>(); + GenericInstantiations = new Dictionary<(MessageOrigin, TypeSystemEntity), TrimAnalysisGenericInstantiationAccessPattern>(); Lattice = lattice; _logger = logger; } @@ -60,8 +62,16 @@ public void Add(TrimAnalysisReflectionAccessPattern pattern) { ReflectionAccessPatterns.TryAdd((pattern.Origin, pattern.Entity), pattern); - // No Merge - there's nothing to merge since this pattern is unequily identified by both the origin and the entity - // and there's only one way to "reflection access" an entity. + // No Merge - there's nothing to merge since this pattern is uniquely identified by both the origin and the entity + // and there's only one way to "access" a generic instantiation. + } + + public void Add(TrimAnalysisGenericInstantiationAccessPattern pattern) + { + GenericInstantiations.TryAdd((pattern.Origin, pattern.Entity), pattern); + + // No Merge - there's nothing to merge since this pattern is uniquely identified by both the origin and the entity + // and there's only one way to "access" a generic instantiation. } public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker) @@ -74,6 +84,9 @@ public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker) foreach (var pattern in ReflectionAccessPatterns.Values) pattern.MarkAndProduceDiagnostics(reflectionMarker, _logger); + + foreach (var pattern in GenericInstantiations.Values) + pattern.MarkAndProduceDiagnostics(reflectionMarker, _logger); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs index 8a07621f56539..8f40b94c02d81 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs @@ -1,9 +1,12 @@ // 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; using ILCompiler.Logging; using Internal.TypeSystem; +#nullable enable + namespace ILCompiler.Dataflow { public readonly record struct TrimAnalysisReflectionAccessPattern @@ -17,12 +20,25 @@ internal TrimAnalysisReflectionAccessPattern(TypeSystemEntity entity, MessageOri Origin = origin; } - // No Merge - there's nothing to merge since this pattern is unequily identified by both the origin and the entity + // No Merge - there's nothing to merge since this pattern is uniquely identified by both the origin and the entity // and there's only one way to "reflection access" an entity. public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker, Logger logger) { - reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, Entity); + switch (Entity) + { + case MethodDesc method: + reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, method); + break; + + case FieldDesc field: + reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, field); + break; + + default: + Debug.Fail($"Unsupported entity for reflection access pattern: {Entity}"); + break; + } } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs index 6d734d92ee6e0..1e9e3144d2be0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs @@ -75,8 +75,8 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact } } - // Ask the metadata manager if we have any dependencies due to reflectability. - factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencyList, factory, _type); + // Ask the metadata manager if we have any dependencies due to the presence of the EEType. + factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencyList, factory, _type); factory.InteropStubManager.AddInterestingInteropConstructedTypeDependencies(ref dependencyList, factory, _type); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs new file mode 100644 index 0000000000000..fb6f59f9a5206 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedTypeDefinitionNode.cs @@ -0,0 +1,93 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.TypeSystem; + +using ILCompiler.Dataflow; +using ILCompiler.DependencyAnalysisFramework; + +using ILLink.Shared.TrimAnalysis; +using ILCompiler.Logging; + +namespace ILCompiler.DependencyAnalysis +{ + public class DataflowAnalyzedTypeDefinitionNode : DependencyNodeCore + { + private readonly TypeDesc _typeDefinition; + + public DataflowAnalyzedTypeDefinitionNode(TypeDesc typeDefinition) + { + Debug.Assert(typeDefinition.IsTypeDefinition); + _typeDefinition = typeDefinition; + } + + public static void GetDependencies(ref DependencyList dependencies, NodeFactory factory, FlowAnnotations flowAnnotations, TypeDesc type) + { + bool foundGenericParameterAnnotation = false; + + type = type.GetTypeDefinition(); + + try + { + if (type.HasBaseType) + { + foundGenericParameterAnnotation |= GenericArgumentDataFlow.RequiresGenericArgumentDataFlow(flowAnnotations, type.BaseType); + } + + if (type is MetadataType metadataType) + { + foreach (var interfaceType in metadataType.ExplicitlyImplementedInterfaces) + { + foundGenericParameterAnnotation |= GenericArgumentDataFlow.RequiresGenericArgumentDataFlow(flowAnnotations, interfaceType); + } + } + } + catch (TypeSystemException) + { + // Wasn't able to do dataflow because of missing references or something like that. + // This likely won't compile either, so we don't care about missing dependencies. + } + + if (foundGenericParameterAnnotation) + { + dependencies ??= new DependencyList(); + dependencies.Add(factory.DataflowAnalyzedTypeDefinition(type), "Generic parameter dataflow"); + } + } + + public override IEnumerable GetStaticDependencies(NodeFactory factory) + { + DependencyList dependencies = null; + + if (_typeDefinition.HasBaseType) + { + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, factory, new MessageOrigin(_typeDefinition), _typeDefinition.BaseType, _typeDefinition); + } + + if (_typeDefinition is MetadataType metadataType) + { + foreach (var interfaceType in metadataType.ExplicitlyImplementedInterfaces) + { + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, factory, new MessageOrigin(_typeDefinition), interfaceType, _typeDefinition); + } + } + + return dependencies; + } + + protected override string GetName(NodeFactory factory) + { + return "Dataflow analysis for type definition " + _typeDefinition.ToString(); + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs index 6f9aebfd9722e..781a633c1aa78 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs @@ -596,8 +596,8 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact if (!ConstructedEETypeNode.CreationAllowed(_type)) { // If necessary MethodTable is the highest load level for this type, ask the metadata manager - // if we have any dependencies due to reflectability. - factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencies, factory, _type); + // if we have any dependencies due to presence of the EEType. + factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencies, factory, _type); // If necessary MethodTable is the highest load level, consider this a module use if(_type is MetadataType mdType) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs index 3c98314c9d9d3..325a7bb794627 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/FieldMetadataNode.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; +using ILCompiler.Dataflow; using ILCompiler.DependencyAnalysisFramework; +using ILCompiler.Logging; using Internal.TypeSystem; @@ -43,6 +45,10 @@ public override IEnumerable GetStaticDependencies(NodeFacto if (_field is EcmaField ecmaField) { DynamicDependencyAttributesOnEntityNode.AddDependenciesDueToDynamicDependencyAttribute(ref dependencies, factory, ecmaField); + + // On a reflectable field, perform generic data flow for the field's type + // This is a compensation for the DI issue described in https://github.com/dotnet/runtime/issues/81358 + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, factory, new MessageOrigin(_field), ecmaField.FieldType, ecmaField.OwningType); } return dependencies; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs index f6397e07c2d03..904475d484c88 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs @@ -24,8 +24,8 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact { DependencyList dependencyList = null; - // Ask the metadata manager if we have any dependencies due to reflectability. - factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencyList, factory, _type); + // Ask the metadata manager if we have any dependencies due to the presence of the EEType. + factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencyList, factory, _type); return dependencyList; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs index c6c63fa649851..bcd3797984d20 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDictionaryNode.cs @@ -142,8 +142,6 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact } } - factory.MetadataManager.GetDependenciesForGenericDictionary(ref result, factory, _owningType); - return result; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs index f52615dc36995..d29fefdccafaa 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; +using ILCompiler.Dataflow; using ILCompiler.DependencyAnalysisFramework; +using ILCompiler.Logging; using Internal.TypeSystem; @@ -51,6 +53,15 @@ public override IEnumerable GetStaticDependencies(NodeFacto if (_method is EcmaMethod ecmaMethod) { DynamicDependencyAttributesOnEntityNode.AddDependenciesDueToDynamicDependencyAttribute(ref dependencies, factory, ecmaMethod); + + // On a reflectable method, perform generic data flow for the return type and all the parameter types + // This is a compensation for the DI issue described in https://github.com/dotnet/runtime/issues/81358 + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, factory, new MessageOrigin(_method), _method.Signature.ReturnType, _method); + + foreach (TypeDesc parameterType in _method.Signature) + { + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, factory, new MessageOrigin(_method), parameterType, _method); + } } return dependencies; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index ba0f5be86ea0f..37400c714162f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -378,6 +378,11 @@ private void CreateNodeCaches() return new DataflowAnalyzedMethodNode(il.MethodIL); }); + _dataflowAnalyzedTypeDefinitions = new NodeCache((TypeDesc type) => + { + return new DataflowAnalyzedTypeDefinitionNode(type); + }); + _dynamicDependencyAttributesOnEntities = new NodeCache((TypeSystemEntity entity) => { return new DynamicDependencyAttributesOnEntityNode(entity); @@ -709,6 +714,13 @@ public DataflowAnalyzedMethodNode DataflowAnalyzedMethod(MethodIL methodIL) return _dataflowAnalyzedMethods.GetOrAdd(new MethodILKey(methodIL)); } + private NodeCache _dataflowAnalyzedTypeDefinitions; + + public DataflowAnalyzedTypeDefinitionNode DataflowAnalyzedTypeDefinition(TypeDesc type) + { + return _dataflowAnalyzedTypeDefinitions.GetOrAdd(type); + } + private NodeCache _dynamicDependencyAttributesOnEntities; public DynamicDependencyAttributesOnEntityNode DynamicDependencyAttributesOnEntity(TypeSystemEntity entity) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs index 5ddb1215b286d..52bc74e44e62f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs @@ -147,7 +147,7 @@ public void LogError(TypeSystemEntity origin, DiagnosticId id, params string[] a internal bool IsWarningSuppressed(int code, MessageOrigin origin) { // This is causing too much noise - // https://github.com/dotnet/runtimelab/issues/1591 + // https://github.com/dotnet/runtime/issues/81156 if (code == 2110 || code == 2111 || code == 2113 || code == 2115) return true; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index 19d8a8a0f99a2..f848eb3496b1f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -449,7 +449,7 @@ protected virtual void GetMetadataDependenciesDueToReflectability(ref Dependency /// /// This method is an extension point that can provide additional metadata-based dependencies to generated EETypes. /// - public void GetDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + public virtual void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) { MetadataCategory category = GetMetadataCategory(type); @@ -907,10 +907,6 @@ public virtual void GetDependenciesForGenericDictionary(ref DependencyList depen { } - public virtual void GetDependenciesForGenericDictionary(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) - { - } - public virtual void NoteOverridingMethod(MethodDesc baseMethod, MethodDesc overridingMethod) { } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 0fa74eb50739b..c29a30a02c397 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -380,6 +380,13 @@ private static bool IsTrimmableAssembly(ModuleDesc assembly) return false; } + public override void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + base.GetDependenciesDueToEETypePresence(ref dependencies, factory, type); + + DataflowAnalyzedTypeDefinitionNode.GetDependencies(ref dependencies, factory, FlowAnnotations, type); + } + public override bool HasConditionalDependenciesDueToEETypePresence(TypeDesc type) { // Note: these are duplicated with the checks in GetConditionalDependenciesDueToEETypePresence @@ -552,27 +559,10 @@ protected override void GetDependenciesDueToMethodCodePresenceInternal(ref Depen if (scanReflection) { - if (methodIL != null && FlowAnnotations.RequiresDataflowAnalysis(method)) + if (methodIL != null && Dataflow.ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForMethodBody(FlowAnnotations, method)) { AddDataflowDependency(ref dependencies, factory, methodIL, "Method has annotated parameters"); } - - if ((method.HasInstantiation && !method.IsCanonicalMethod(CanonicalFormKind.Any))) - { - MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); - Debug.Assert(typicalMethod != method); - - GetFlowDependenciesForInstantiation(ref dependencies, factory, method.Instantiation, typicalMethod.Instantiation, method); - } - - TypeDesc owningType = method.OwningType; - if (owningType.HasInstantiation && !owningType.IsCanonicalSubtype(CanonicalFormKind.Any)) - { - TypeDesc owningTypeDefinition = owningType.GetTypeDefinition(); - Debug.Assert(owningType != owningTypeDefinition); - - GetFlowDependenciesForInstantiation(ref dependencies, factory, owningType.Instantiation, owningTypeDefinition.Instantiation, owningType); - } } if (method.GetTypicalMethodDefinition() is Internal.TypeSystem.Ecma.EcmaMethod ecmaMethod) @@ -753,52 +743,8 @@ public override DependencyList GetDependenciesForCustomAttribute(NodeFactory fac return null; } - private void GetFlowDependenciesForInstantiation(ref DependencyList dependencies, NodeFactory factory, Instantiation instantiation, Instantiation typicalInstantiation, TypeSystemEntity source) - { - for (int i = 0; i < instantiation.Length; i++) - { - var genericParameter = (GenericParameterDesc)typicalInstantiation[i]; - if (FlowAnnotations.GetGenericParameterAnnotation(genericParameter) != default) - { - try - { - var deps = (new ILCompiler.Dataflow.GenericArgumentDataFlow(Logger, factory, FlowAnnotations, new Logging.MessageOrigin(source))).ProcessGenericArgumentDataFlow(genericParameter, instantiation[i]); - if (deps.Count > 0) - { - if (dependencies == null) - dependencies = deps; - else - dependencies.AddRange(deps); - } - } - catch (TypeSystemException) - { - // Wasn't able to do dataflow because of missing references or something like that. - // This likely won't compile either, so we don't care about missing dependencies. - } - } - } - } - public override void GetDependenciesForGenericDictionary(ref DependencyList dependencies, NodeFactory factory, MethodDesc method) { - TypeDesc owningType = method.OwningType; - - if (FlowAnnotations.HasAnyAnnotations(owningType)) - { - MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); - Debug.Assert(typicalMethod != method); - - GetFlowDependenciesForInstantiation(ref dependencies, factory, method.Instantiation, typicalMethod.Instantiation, method); - - if (owningType.HasInstantiation) - { - // Since this also introduces a new type instantiation into the system, collect the dependencies for that too. - // We might not see the instantiated type elsewhere. - GetFlowDependenciesForInstantiation(ref dependencies, factory, owningType.Instantiation, owningType.GetTypeDefinition().Instantiation, method); - } - } - // Presence of code might trigger the reflectability dependencies. if ((_generationOptions & UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts) != 0) { @@ -806,16 +752,6 @@ public override void GetDependenciesForGenericDictionary(ref DependencyList depe } } - public override void GetDependenciesForGenericDictionary(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) - { - if (FlowAnnotations.HasAnyAnnotations(type)) - { - TypeDesc typeDefinition = type.GetTypeDefinition(); - Debug.Assert(type != typeDefinition); - GetFlowDependenciesForInstantiation(ref dependencies, factory, type.Instantiation, typeDefinition.Instantiation, type); - } - } - public bool GeneratesAttributeMetadata(TypeDesc attributeType) { var ecmaType = attributeType.GetTypeDefinition() as EcmaType; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 0cc8c90ca0144..631d804cbd986 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -379,6 +379,7 @@ + @@ -387,6 +388,7 @@ + diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs new file mode 100644 index 0000000000000..dace9d177887c --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs @@ -0,0 +1,892 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using BindingFlags = System.Reflection.BindingFlags; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + // NativeAOT differences in behavior: + // + // See the description on top of GenericParameterWarningLocation for the expected differences in behavior + // for NativeAOT. + // The tests affected by this are marked with "NativeAOT_StorageSpaceType" + // + + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class GenericParameterDataFlow + { + public static void Main () + { + TestSingleGenericParameterOnType (); + TestMultipleGenericParametersOnType (); + TestBaseTypeGenericRequirements (); + TestDeepNestedTypesWithGenerics (); + TestInterfaceTypeGenericRequirements (); + TestTypeGenericRequirementsOnMembers (); + TestPartialInstantiationTypes (); + + TestSingleGenericParameterOnMethod (); + TestMultipleGenericParametersOnMethod (); + TestMethodGenericParametersViaInheritance (); + + TestNewConstraintSatisfiesParameterlessConstructor (); + TestStructConstraintSatisfiesParameterlessConstructor (); + TestUnmanagedConstraintSatisfiesParameterlessConstructor (); + + TestGenericParameterFlowsToField (); + TestGenericParameterFlowsToReturnValue (); + + TestNoWarningsInRUCMethod (); + TestNoWarningsInRUCType (); + } + + static void TestSingleGenericParameterOnType () + { + TypeRequiresNothing.Test (); + TypeRequiresPublicFields.Test (); + TypeRequiresPublicMethods.Test (); + TypeRequiresPublicFieldsPassThrough.Test (); + TypeRequiresNothingPassThrough.Test (); + } + + static void TestGenericParameterFlowsToReturnValue () + { + _ = TypeRequiresPublicFields.ReturnRequiresPublicFields (); + _ = TypeRequiresPublicFields.ReturnRequiresPublicMethods (); + _ = TypeRequiresPublicFields.ReturnRequiresNothing (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + static Type FieldRequiresPublicFields; + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static Type FieldRequiresPublicMethods; + + static Type FieldRequiresNothing; + + + class TypeRequiresPublicFields< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> + { + [ExpectedWarning ("IL2087", "'" + nameof (T) + "'", nameof (TypeRequiresPublicFields), nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + public static void Test () + { + typeof (T).RequiresPublicFields (); + typeof (T).RequiresPublicMethods (); + typeof (T).RequiresNone (); + } + + [RequiresUnreferencedCode ("message")] + public static void RUCTest () + { + typeof (T).RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2089", "'" + nameof (T) + "'", nameof (TypeRequiresPublicFields), nameof (FieldRequiresPublicMethods))] + public static void TestFields () + { + FieldRequiresPublicFields = typeof (T); + FieldRequiresPublicMethods = typeof (T); + FieldRequiresNothing = typeof (T); + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + public static Type ReturnRequiresPublicFields () + { + return typeof (T); + } + + [ExpectedWarning ("IL2088", "'" + nameof (T) + "'", nameof (TypeRequiresPublicFields), nameof (ReturnRequiresPublicMethods))] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public static Type ReturnRequiresPublicMethods () + { + return typeof (T); + } + public static Type ReturnRequiresNothing () + { + return typeof (T); + } + } + + class TypeRequiresPublicMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> + { + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + public static void Test () + { + typeof (T).RequiresPublicFields (); + typeof (T).RequiresPublicMethods (); + typeof (T).RequiresNone (); + } + } + + class TypeRequiresNothing + { + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + public static void Test () + { + typeof (T).RequiresPublicFields (); + typeof (T).RequiresPublicMethods (); + typeof (T).RequiresNone (); + } + } + + class TypeRequiresPublicFieldsPassThrough< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TSource> + { + [ExpectedWarning ("IL2091", nameof (TSource), + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeRequiresPublicFieldsPassThrough", + "T", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeRequiresPublicMethods")] + public static void Test () + { + TypeRequiresPublicFields.Test (); + TypeRequiresPublicMethods.Test (); + TypeRequiresNothing.Test (); + } + } + + class TypeRequiresNothingPassThrough + { + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicFields))] + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods))] + public static void Test () + { + TypeRequiresPublicFields.Test (); + TypeRequiresPublicMethods.Test (); + TypeRequiresNothing.Test (); + } + } + + static void TestBaseTypeGenericRequirements () + { + new DerivedTypeWithInstantiatedGenericOnBase (); + new DerivedTypeWithInstantiationOverSelfOnBase (); + new DerivedTypeWithOpenGenericOnBase (); + TestDerivedTypeWithOpenGenericOnBaseWithRUCOnBase (); + TestDerivedTypeWithOpenGenericOnBaseWithRUCOnDerived (); + new DerivedTypeWithOpenGenericOnBaseWithRequirements (); + } + + /// + /// Adding a comment to verify that analyzer doesn't null ref when trying to analyze + /// generic parameter in cref comments on a class + /// + /// + class GenericBaseTypeWithRequirements<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> + { + public GenericBaseTypeWithRequirements () + { + typeof (T).RequiresPublicFields (); + } + } + + class DerivedTypeWithInstantiatedGenericOnBase : GenericBaseTypeWithRequirements + { + } + + class DerivedTypeWithInstantiationOverSelfOnBase : GenericBaseTypeWithRequirements + { + } + + [ExpectedWarning ("IL2091", nameof (GenericBaseTypeWithRequirements))] + class DerivedTypeWithOpenGenericOnBase : GenericBaseTypeWithRequirements + { + // Analyzer does not see the base class constructor + [ExpectedWarning ("IL2091", nameof (GenericBaseTypeWithRequirements), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public DerivedTypeWithOpenGenericOnBase () { } + } + + static void TestDerivedTypeWithOpenGenericOnBaseWithRUCOnBase () + { + new DerivedTypeWithOpenGenericOnBaseWithRUCOnBase (); + } + + // https://github.com/dotnet/runtime/issues/81158 + [ExpectedWarning ("IL2109", nameof (BaseTypeWithOpenGenericDAMTAndRUC), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [ExpectedWarning ("IL2091", nameof (BaseTypeWithOpenGenericDAMTAndRUC))] + class DerivedTypeWithOpenGenericOnBaseWithRUCOnBase : BaseTypeWithOpenGenericDAMTAndRUC + { + [ExpectedWarning ("IL2091", nameof (DerivedTypeWithOpenGenericOnBaseWithRUCOnBase), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2026", nameof (BaseTypeWithOpenGenericDAMTAndRUC), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public DerivedTypeWithOpenGenericOnBaseWithRUCOnBase () { } + } + + [RequiresUnreferencedCode ("RUC")] + class BaseTypeWithOpenGenericDAMTAndRUC<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T> { } + + + [ExpectedWarning ("IL2026", nameof (DerivedTypeWithOpenGenericOnBaseWithRUCOnDerived))] + static void TestDerivedTypeWithOpenGenericOnBaseWithRUCOnDerived () + { + new DerivedTypeWithOpenGenericOnBaseWithRUCOnDerived (); + } + [ExpectedWarning ("IL2091", nameof (BaseTypeWithOpenGenericDAMT))] + [RequiresUnreferencedCode ("RUC")] + class DerivedTypeWithOpenGenericOnBaseWithRUCOnDerived : BaseTypeWithOpenGenericDAMT + { + } + + class BaseTypeWithOpenGenericDAMT<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T> { } + + + class DerivedTypeWithOpenGenericOnBaseWithRequirements<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> + : GenericBaseTypeWithRequirements + { + } + + static void TestMultipleGenericParametersOnType () + { + MultipleTypesWithDifferentRequirements.TestMultiple (); + MultipleTypesWithDifferentRequirements.TestFields (); + MultipleTypesWithDifferentRequirements.TestMethods (); + MultipleTypesWithDifferentRequirements.TestBoth (); + MultipleTypesWithDifferentRequirements.TestNothing (); + } + + class MultipleTypesWithDifferentRequirements< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods)] TBoth, + TNothing> + { + public static void TestMultiple () + { + typeof (TFields).RequiresPublicFields (); + typeof (TMethods).RequiresPublicMethods (); + typeof (TBoth).RequiresPublicFields (); + typeof (TBoth).RequiresPublicMethods (); + typeof (TFields).RequiresNone (); + typeof (TMethods).RequiresNone (); + typeof (TBoth).RequiresNone (); + typeof (TNothing).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + public static void TestFields () + { + typeof (TFields).RequiresPublicFields (); + typeof (TFields).RequiresPublicMethods (); + typeof (TFields).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + public static void TestMethods () + { + typeof (TMethods).RequiresPublicFields (); + typeof (TMethods).RequiresPublicMethods (); + typeof (TMethods).RequiresNone (); + } + + public static void TestBoth () + { + typeof (TBoth).RequiresPublicFields (); + typeof (TBoth).RequiresPublicMethods (); + typeof (TBoth).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + public static void TestNothing () + { + typeof (TNothing).RequiresPublicFields (); + typeof (TNothing).RequiresPublicMethods (); + typeof (TNothing).RequiresNone (); + } + } + + static void TestDeepNestedTypesWithGenerics () + { + RootTypeWithRequirements.InnerTypeWithNoAddedGenerics.TestAccess (); + } + + class RootTypeWithRequirements<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TRoot> + { + public class InnerTypeWithNoAddedGenerics + { + [ExpectedWarning ("IL2087", nameof (TRoot), + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.RootTypeWithRequirements", + "type", + "DataFlowTypeExtensions.RequiresPublicMethods(Type)")] + public static void TestAccess () + { + typeof (TRoot).RequiresPublicFields (); + typeof (TRoot).RequiresPublicMethods (); + } + } + } + + static void TestInterfaceTypeGenericRequirements () + { + IGenericInterfaceTypeWithRequirements instance = new InterfaceImplementationTypeWithInstantiatedGenericOnBase (); + new InterfaceImplementationTypeWithInstantiationOverSelfOnBase (); + new InterfaceImplementationTypeWithOpenGenericOnBase (); + new InterfaceImplementationTypeWithOpenGenericOnBaseWithRequirements (); + + RecursiveGenericWithInterfacesRequirement.Test (); + } + + interface IGenericInterfaceTypeWithRequirements<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> + { + } + + class InterfaceImplementationTypeWithInstantiatedGenericOnBase : IGenericInterfaceTypeWithRequirements + { + } + + interface IGenericInterfaceTypeWithRequiresAll<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T> + { + } + + class InterfaceImplementationTypeWithInstantiationOverSelfOnBase : IGenericInterfaceTypeWithRequiresAll + { + } + + [ExpectedWarning ("IL2091", nameof (IGenericInterfaceTypeWithRequirements))] + class InterfaceImplementationTypeWithOpenGenericOnBase : IGenericInterfaceTypeWithRequirements + { + } + class InterfaceImplementationTypeWithOpenGenericOnBaseWithRequirements<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> + : IGenericInterfaceTypeWithRequirements + { + } + + class RecursiveGenericWithInterfacesRequirement + { + interface IFace<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] T> + { + } + + class TestType : IFace + { + } + + public static void Test () + { + var a = typeof (IFace); + var t = new TestType (); + } + } + + static void TestTypeGenericRequirementsOnMembers () + { + // Basically just root everything we need to test + var instance = new TypeGenericRequirementsOnMembers (); + + _ = instance.PublicFieldsField; + _ = instance.PublicMethodsField; + + _ = instance.PublicFieldsProperty; + instance.PublicFieldsProperty = null; + _ = instance.PublicMethodsProperty; + instance.PublicMethodsProperty = null; + + instance.PublicFieldsMethodParameter (null); + instance.PublicMethodsMethodParameter (null); + + instance.PublicFieldsMethodReturnValue (); + instance.PublicMethodsMethodReturnValue (); + + instance.PublicFieldsMethodLocalVariable (); + instance.PublicMethodsMethodLocalVariable (); + } + + class TypeGenericRequirementsOnMembers<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TOuter> + { + public TypeRequiresPublicFields PublicFieldsField; + + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + public TypeRequiresPublicMethods PublicMethodsField; + + public TypeRequiresPublicFields PublicFieldsProperty { + get; + set; + } + + [ExpectedWarning ("IL2091", nameof (TypeGenericRequirementsOnMembers), ProducedBy = ProducedBy.Analyzer)] + public TypeRequiresPublicMethods PublicMethodsProperty { + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer)] // NativeAOT_StorageSpaceType + get => null; + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer)] // NativeAOT_StorageSpaceType + set { } + } + + public void PublicFieldsMethodParameter (TypeRequiresPublicFields param) { } + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + public void PublicMethodsMethodParameter (TypeRequiresPublicMethods param) { } + + public TypeRequiresPublicFields PublicFieldsMethodReturnValue () { return null; } + + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + public TypeRequiresPublicMethods PublicMethodsMethodReturnValue () { return null; } + + public void PublicFieldsMethodLocalVariable () + { + TypeRequiresPublicFields t = null; + } + + [ExpectedWarning ("IL2091", nameof (TypeRequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + public void PublicMethodsMethodLocalVariable () + { + TypeRequiresPublicMethods t = null; + } + } + + static void TestPartialInstantiationTypes () + { + _ = new PartialyInstantiatedFields (); + _ = new FullyInstantiatedOverPartiallyInstantiatedFields (); + _ = new PartialyInstantiatedMethods (); + _ = new FullyInstantiatedOverPartiallyInstantiatedMethods (); + } + + class BaseForPartialInstantiation< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods> + { + } + + class PartialyInstantiatedFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TOuter> + : BaseForPartialInstantiation + { + } + + class FullyInstantiatedOverPartiallyInstantiatedFields + : PartialyInstantiatedFields + { + } + + [ExpectedWarning ("IL2091", nameof (BaseForPartialInstantiation), "'TMethods'")] + class PartialyInstantiatedMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TOuter> + : BaseForPartialInstantiation + { + // Analyzer does not see the base class constructor + [ExpectedWarning ("IL2091", nameof (BaseForPartialInstantiation), "'TMethods'", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public PartialyInstantiatedMethods () { } + } + + class FullyInstantiatedOverPartiallyInstantiatedMethods + : PartialyInstantiatedMethods + { + } + + static void TestSingleGenericParameterOnMethod () + { + MethodRequiresPublicFields (); + MethodRequiresPublicMethods (); + MethodRequiresNothing (); + MethodRequiresPublicFieldsPassThrough (); + MethodRequiresNothingPassThrough (); + } + + /// + /// Adding a comment to verify that analyzer doesn't null ref when trying to analyze + /// generic parameter comments on a method + /// + /// + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void MethodRequiresPublicFields< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + { + typeof (T).RequiresPublicFields (); + typeof (T).RequiresPublicMethods (); + typeof (T).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + static void MethodRequiresPublicMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { + typeof (T).RequiresPublicFields (); + typeof (T).RequiresPublicMethods (); + typeof (T).RequiresNone (); + } + + [RequiresUnreferencedCode ("message")] + static void RUCMethodRequiresPublicMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { + typeof (T).RequiresPublicFields (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void MethodRequiresNothing () + { + typeof (T).RequiresPublicFields (); + typeof (T).RequiresPublicMethods (); + typeof (T).RequiresNone (); + } + + [ExpectedWarning ("IL2091", nameof (MethodRequiresPublicMethods), "'T'")] + static void MethodRequiresPublicFieldsPassThrough< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + { + MethodRequiresPublicFields (); + MethodRequiresPublicMethods (); + MethodRequiresNothing (); + } + + [ExpectedWarning ("IL2091", nameof (MethodRequiresPublicFields), "'T'")] + [ExpectedWarning ("IL2091", nameof (MethodRequiresPublicMethods), "'T'")] + static void MethodRequiresNothingPassThrough () + { + MethodRequiresPublicFields (); + MethodRequiresPublicMethods (); + MethodRequiresNothing (); + } + + static void TestMultipleGenericParametersOnMethod () + { + MethodMultipleWithDifferentRequirements_TestMultiple (); + MethodMultipleWithDifferentRequirements_TestFields (); + MethodMultipleWithDifferentRequirements_TestMethods (); + MethodMultipleWithDifferentRequirements_TestBoth (); + MethodMultipleWithDifferentRequirements_TestNothing (); + } + + static void MethodMultipleWithDifferentRequirements_TestMultiple< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods)] TBoth, + TNothing> () + { + typeof (TFields).RequiresPublicFields (); ; + typeof (TMethods).RequiresPublicMethods (); + typeof (TBoth).RequiresPublicFields (); ; + typeof (TBoth).RequiresPublicMethods (); + typeof (TFields).RequiresNone (); + typeof (TMethods).RequiresNone (); + typeof (TBoth).RequiresNone (); + typeof (TNothing).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void MethodMultipleWithDifferentRequirements_TestFields< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods)] TBoth, + TNothing> () + { + typeof (TFields).RequiresPublicFields (); ; + typeof (TFields).RequiresPublicMethods (); + typeof (TFields).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + static void MethodMultipleWithDifferentRequirements_TestMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods)] TBoth, + TNothing> () + { + typeof (TMethods).RequiresPublicFields (); + typeof (TMethods).RequiresPublicMethods (); + typeof (TMethods).RequiresNone (); + } + + static void MethodMultipleWithDifferentRequirements_TestBoth< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods)] TBoth, + TNothing> () + { + typeof (TBoth).RequiresPublicFields (); + typeof (TBoth).RequiresPublicMethods (); + typeof (TBoth).RequiresNone (); + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void MethodMultipleWithDifferentRequirements_TestNothing< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicMethods)] TBoth, + TNothing> () + { + typeof (TNothing).RequiresPublicFields (); + typeof (TNothing).RequiresPublicMethods (); + typeof (TNothing).RequiresNone (); + } + + static void TestMethodGenericParametersViaInheritance () + { + TypeWithInstantiatedGenericMethodViaGenericParameter.StaticRequiresPublicFields (); + TypeWithInstantiatedGenericMethodViaGenericParameter.StaticRequiresPublicFieldsNonGeneric (); + + TypeWithInstantiatedGenericMethodViaGenericParameter.StaticPartialInstantiation (); + TypeWithInstantiatedGenericMethodViaGenericParameter.StaticPartialInstantiationUnrecognized (); + + var instance = new TypeWithInstantiatedGenericMethodViaGenericParameter (); + + instance.InstanceRequiresPublicFields (); + instance.InstanceRequiresPublicFieldsNonGeneric (); + + instance.VirtualRequiresPublicFields (); + instance.VirtualRequiresPublicMethods (); + + instance.CallInterface (); + + IInterfaceWithGenericMethod interfaceInstance = (IInterfaceWithGenericMethod) instance; + interfaceInstance.InterfaceRequiresPublicFields (); + interfaceInstance.InterfaceRequiresPublicMethods (); + } + + class BaseTypeWithGenericMethod + { + public static void StaticRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + => typeof (T).RequiresPublicFields (); + public void InstanceRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + => typeof (T).RequiresPublicFields (); + public virtual void VirtualRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + => typeof (T).RequiresPublicFields (); + + public static void StaticRequiresPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + => typeof (T).RequiresPublicMethods (); + public void InstanceRequiresPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + => typeof (T).RequiresPublicMethods (); + public virtual void VirtualRequiresPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + => typeof (T).RequiresPublicMethods (); + + public static void StaticRequiresMultipleGenericParams< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods> () + { + typeof (TFields).RequiresPublicFields (); + typeof (TMethods).RequiresPublicMethods (); + } + } + + interface IInterfaceWithGenericMethod + { + void InterfaceRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> (); + void InterfaceRequiresPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> (); + } + + + class TypeWithInstantiatedGenericMethodViaGenericParameter<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TOuter> + : BaseTypeWithGenericMethod, IInterfaceWithGenericMethod + { + [ExpectedWarning ("IL2091", + "'TInner'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter.StaticRequiresPublicFields()", + "'T'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.BaseTypeWithGenericMethod.StaticRequiresPublicMethods()")] + public static void StaticRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TInner> () + { + StaticRequiresPublicFields (); + StaticRequiresPublicMethods (); + } + + [ExpectedWarning ("IL2091", + "'TOuter'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter", + "'T'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.BaseTypeWithGenericMethod.StaticRequiresPublicMethods()")] + public static void StaticRequiresPublicFieldsNonGeneric () + { + StaticRequiresPublicFields (); + StaticRequiresPublicMethods (); + } + + public static void StaticPartialInstantiation () + { + StaticRequiresMultipleGenericParams (); + } + + [ExpectedWarning ("IL2091", + nameof (TOuter), + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter", + "TMethods", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.BaseTypeWithGenericMethod.StaticRequiresMultipleGenericParams()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2091", + "'TOuter'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter", + "'TMethods'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.BaseTypeWithGenericMethod.StaticRequiresMultipleGenericParams", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public static void StaticPartialInstantiationUnrecognized () + { + StaticRequiresMultipleGenericParams (); + } + + [ExpectedWarning ("IL2091", + "'TInner'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter", "InstanceRequiresPublicFields", + "'T'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.BaseTypeWithGenericMethod.InstanceRequiresPublicMethods")] + public void InstanceRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TInner> () + { + InstanceRequiresPublicFields (); + InstanceRequiresPublicMethods (); + } + + [ExpectedWarning ("IL2091", + "'TOuter'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter", + "'T'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.BaseTypeWithGenericMethod.InstanceRequiresPublicMethods")] + public void InstanceRequiresPublicFieldsNonGeneric () + { + InstanceRequiresPublicFields (); + InstanceRequiresPublicMethods (); + } + + public override void VirtualRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + { + typeof (T).RequiresPublicFields (); + } + + public override void VirtualRequiresPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { + typeof (T).RequiresPublicMethods (); + } + + public void InterfaceRequiresPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> () + { + typeof (T).RequiresPublicFields (); ; + } + + public void InterfaceRequiresPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { + typeof (T).RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2091", + "'TOuter'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.TypeWithInstantiatedGenericMethodViaGenericParameter", + "'T'", + "Mono.Linker.Tests.Cases.DataFlow.GenericParameterDataFlow.IInterfaceWithGenericMethod.InterfaceRequiresPublicMethods")] + public void CallInterface () + { + IInterfaceWithGenericMethod interfaceInstance = (IInterfaceWithGenericMethod) this; + interfaceInstance.InterfaceRequiresPublicFields (); + interfaceInstance.InterfaceRequiresPublicMethods (); + } + } + + static void TestNewConstraintSatisfiesParameterlessConstructor () where T : new() + { + RequiresParameterlessConstructor (); + } + + static void TestStructConstraintSatisfiesParameterlessConstructor () where T : struct + { + RequiresParameterlessConstructor (); + } + static void TestUnmanagedConstraintSatisfiesParameterlessConstructor () where T : unmanaged + { + RequiresParameterlessConstructor (); + } + + static void RequiresParameterlessConstructor<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T> () + { + } + + // Warn about calls to static methods: + [ExpectedWarning ("IL2026", "TypeRequiresPublicFields", "RUCTest()", "message")] + [ExpectedWarning ("IL2026", "RUCMethodRequiresPublicMethods", "message")] + // And about type/method generic parameters on the RUC methods: + [ExpectedWarning ("IL2091", "TypeRequiresPublicFields")] + [ExpectedWarning ("IL2091", "RUCMethodRequiresPublicMethods")] + static void TestNoWarningsInRUCMethod () + { + TypeRequiresPublicFields.RUCTest (); + RUCMethodRequiresPublicMethods (); + } + + // Warn about calls to the static methods and the ctor on the RUC type: + [ExpectedWarning ("IL2026", "StaticMethod", "message")] + [ExpectedWarning ("IL2026", "StaticMethodRequiresPublicMethods", "message")] + [ExpectedWarning ("IL2026", "StaticMethodRequiresPublicMethods", "message")] + [ExpectedWarning ("IL2026", "StaticMethodRequiresPublicMethods", "message")] + [ExpectedWarning ("IL2026", "RUCTypeRequiresPublicFields", "message")] + // And about method generic parameters: + [ExpectedWarning ("IL2091", "InstanceMethodRequiresPublicMethods")] + [ExpectedWarning ("IL2091", "StaticMethodRequiresPublicMethods")] + [ExpectedWarning ("IL2091", "StaticMethodRequiresPublicMethods")] + [ExpectedWarning ("IL2091", "VirtualMethodRequiresPublicMethods")] + // And about type generic parameters: (one for each reference to the type): + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields")] // StaticMethod + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields")] // StaticMethodRequiresPublicMethods + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields")] // StaticMethodRequiresPublicMethods + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields")] // RUCTypeRequiresPublicFields ctor + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields", ProducedBy = ProducedBy.Trimmer)] // RUCTypeRequiresPublicFields local, // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields", ProducedBy = ProducedBy.Trimmer)] // InstanceMethod, // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] // InstanceMethodRequiresPublicMethods + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields", ProducedBy = ProducedBy.Trimmer)] // VirtualMethod, // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", "RUCTypeRequiresPublicFields", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] // VirtualMethodRequiresPublicMethods + static void TestNoWarningsInRUCType () + { + RUCTypeRequiresPublicFields.StaticMethod (); + RUCTypeRequiresPublicFields.StaticMethodRequiresPublicMethods (); + RUCTypeRequiresPublicFields.StaticMethodRequiresPublicMethods (); + RUCTypeRequiresPublicFields.StaticMethodRequiresPublicMethods (); + var rucType = new RUCTypeRequiresPublicFields (); + rucType.InstanceMethod (); + rucType.InstanceMethodRequiresPublicMethods (); + rucType.VirtualMethod (); + rucType.VirtualMethodRequiresPublicMethods (); + } + + [RequiresUnreferencedCode ("message")] + public class RUCTypeRequiresPublicFields< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> + { + public static void StaticMethod () + { + typeof (T).RequiresPublicMethods (); + } + + public static void StaticMethodRequiresPublicMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] U> () + { + typeof (U).RequiresPublicFields (); + } + + public void InstanceMethod () + { + typeof (T).RequiresPublicMethods (); + } + + public void InstanceMethodRequiresPublicMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] U> () + { + typeof (U).RequiresPublicFields (); + } + + public virtual void VirtualMethod () + { + typeof (T).RequiresPublicMethods (); + } + + public virtual void VirtualMethodRequiresPublicMethods< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] U> () + { + typeof (U).RequiresPublicFields (); + } + } + + static void TestGenericParameterFlowsToField () + { + TypeRequiresPublicFields.TestFields (); + } + + public class TestType + { + } + public struct TestStruct + { + } + + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlowMarking.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlowMarking.cs new file mode 100644 index 0000000000000..8710aec6281b5 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlowMarking.cs @@ -0,0 +1,331 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [ExpectedNoWarnings] + public class GenericParameterDataFlowMarking + { + public static void Main () + { + NestedGenerics.Test (); + } + + [Kept] + class NestedGenerics + { + [Kept] + interface IUse { void Use (); } + + [Kept] + class RequiresMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> : IUse + { + [Kept] + public void Use () { } + } + + [Kept] + class RequiresNothing : IUse + { + [Kept] + public void Use () { } + } + + [Kept] + class RequiresFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> : IUse + { + [Kept] + public void Use () { } + } + + [Kept] + class GenericMethodNoReference + { + [Kept] + static void GenericMethod () { } + + [Kept] + class TargetTypeForNothing + { + public int PublicField; + public static void PublicMethod () { } + static void PrivateMethod () { } + } + + [Kept] + class TargetType + { + public int PublicField; + [Kept] // This is technically not necessary, but the complexity of the implementation would be much higher + public static void PublicMethod () { } + static void PrivateMethod () { } + } + + [Kept] + public static void Test() + { + GenericMethod>> (); + GenericMethod>>> (); + } + } + + [Kept] + class GenericMethodCallReference + { + [Kept] + static void GenericMethod (T value) where T : IUse { value.Use (); } + + [Kept] + class TargetTypeForNothing : IUse + { + public int PublicField; + public static void PublicMethod () { } + static void PrivateMethod () { } + + public void Use () { } + } + + [Kept] + class TargetType : IUse + { + public int PublicField; + [Kept] + public static void PublicMethod () { } + static void PrivateMethod () { } + + [Kept] + public void Use () { } + } + + [Kept] + public static void Test () + { + GenericMethod>> (null); + GenericMethod>>> (null); + } + } + + [Kept] + class GenericMethodReflectionReference + { + [Kept] + static void GenericMethod () { _ = typeof (T).Name; } + + [Kept] + class TargetTypeForNothing + { + public int PublicField; + public static void PublicMethod () { } + static void PrivateMethod () { } + } + + [Kept] + class TargetType + { + public int PublicField; + [Kept] + public static void PublicMethod () { } + static void PrivateMethod () { } + } + + [Kept] + public static void Test () + { + GenericMethod>> (); + GenericMethod>>> (); + } + } + + [Kept] + [KeptMember(".ctor()")] + class GenericInstanceMethod + { + [Kept] + void GenericMethod () { _ = typeof (T).Name; } + + [Kept] + class TargetTypeForNothing + { + public static void PublicMethod () { } + } + + [Kept] + class TargetType + { + [Kept] + public static void PublicMethod () { } + } + + [Kept] + public static void Test () + { + GenericInstanceMethod instance = new (); + instance.GenericMethod>> (); + instance.GenericMethod>>>> (); + } + } + + [Kept] + class GenericMethodOnGenericType + { + class GenericType + { + [Kept] + public static void GenericMethod () { _ = typeof (TType).Name + typeof (TMethod).Name; } + } + + [Kept] + class TargetTypeForTType + { + public int Field; + [Kept] + public static void PublicMethod () { } + } + + [Kept] + class TargetTypeForTMethod + { + [Kept] + public int Field; + public static void PublicMethod () { } + } + + [Kept] + public static void Test () + { + GenericType>>> + .GenericMethod>>> (); + } + } + + [Kept] + class MethodOnGenericType + { + class GenericType + { + [Kept] + public static void Method () { _ = typeof (TType).Name; } + } + + [Kept] + class TargetTypeForTType + { + public int Field; + [Kept] + public static void PublicMethod () { } + } + + [Kept] + public static void Test () + { + GenericType>>> + .Method (); + } + } + + [Kept] + class FieldOnGenericType + { + class GenericType + { + // NativeAOT will not preserve any information about the type or field + // the access to the field will be optimized as just a write to a memory location. + [Kept (By = ProducedBy.Trimmer)] + public static int Field; + } + + [Kept] + class TargetTypeForTType + { + public int Field; + [Kept] + public static void PublicMethod () { } + } + + [Kept] + public static void Test () + { + GenericType>>> + .Field = 0; + } + } + + [Kept] + class BaseTypeGenericNesting + { + [Kept] + class Base + { + } + + [Kept] + class TargetType + { + public int Field; + [Kept] + public static void PublicMethod () { } + } + + [Kept] + class DerivedWithTarget + : Base>>> + { } + + [Kept] + public static void Test () + { + Type a; + a = typeof (DerivedWithTarget); + } + } + + [Kept] + class InterfaceGenericNesting + { + [Kept] + class IBase + { + } + + [Kept] + class TargetType + { + public int Field; + [Kept] + public static void PublicMethod () { } + } + + [Kept] + class DerivedWithTarget + : IBase>>> + { } + + [Kept] + public static void Test () + { + Type a; + a = typeof (DerivedWithTarget); + } + } + + [Kept] + public static void Test () + { + GenericMethodNoReference.Test (); + GenericMethodCallReference.Test (); + GenericMethodReflectionReference.Test (); + GenericInstanceMethod.Test (); + GenericMethodOnGenericType.Test (); + MethodOnGenericType.Test (); + FieldOnGenericType.Test (); + BaseTypeGenericNesting.Test (); + InterfaceGenericNesting.Test (); + } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs new file mode 100644 index 0000000000000..74b5d596c6c98 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs @@ -0,0 +1,1569 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using System.Security.Policy; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + // NativeAOT differences in behavior: + // + // Validation of generic parameters only matters if the instantiation can be used to run code with the substituted type. + // So for generic methods the validation has to happen basically always (since any access to the method can lead to the code + // of the method executing eventually). + // For generic types though the situation is different. Code on the type can only run if the type is instantiated (new) + // or if static members are accessed on it (method calls, or fields accesses both can lead to static .cctor execution). + // Others usages of the type cannot themselves lead to code execution in the type, and thus don't need to be validated. + // Currently linker and analyzer both validate every time there's a type occurrence in the code. + // NativeAOT on the other hand only validates the cases which can lead to code execution (this is partially because the compiler + // doesn't care about the type in other situations really). + // So for example local variables of a given type, or method parameters of that type alone will not cause code execution + // inside that type and thus won't be validated by NativeAOT compiler. + // + // Below this explanation/fact is referred to as "NativeAOT_StorageSpaceType" + // Storage space - declaring a storage space as having a specific type doesn't in itself do anything with that type as per + // the above description. + + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class GenericParameterWarningLocation + { + public static void Main () + { + TypeInheritance.Test (); + TypeImplementingInterface.Test (); + MethodParametersAndReturn.Test (); + MethodParametersAndReturnAccessedViaReflection.Test (); + FieldDefinition.Test (); + FieldDefinitionViaReflection.Test (); + PropertyDefinition.Test (); + MethodBody.Test (); + GenericAttributes.Test (); + NestedGenerics.Test (); + } + + class TypeInheritance + { + class BaseWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { + public static void GetMethods () + { + typeof (TPublicMethods).GetMethods (); + } + } + + class BaseWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + // No warning - annotations applied + class DerivedWithSpecificType : BaseWithPublicMethods { } + + // No warning - annotations match + class DerivedWithMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] TAllMethods> + : BaseWithPublicMethods + { } + + [ExpectedWarning ("IL2091")] + class DerivedWithNoAnnotations + : BaseWithPublicMethods + { + [ExpectedWarning ("IL2091")] // Compiler generates an implicit call to BaseWithPublicMethods..ctor + public DerivedWithNoAnnotations () { } + } + + [ExpectedWarning ("IL2091")] + class DerivedWithMismatchAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + : BaseWithPublicMethods + { } + + [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicMethods))] + class DerivedWithOneMismatch<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + : BaseWithTwo + { } + + class DerivedWithTwoMatching< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + : BaseWithTwo + { } + + [ExpectedWarning ("IL2091")] + class DerivedWithOnlyStaticMethodReference : BaseWithPublicMethods + { + // The method body in this case looks like: + // BaseWithPublicMethods.GetMethods () + // The type instantiation needs to be validated and in this case it produces a warning. + // This is no different from the same code being part of a completely unrelated method/class. + // So the fact that this is in derived class has no impact on the validation in this case. + [ExpectedWarning ("IL2091")] + public static void GetDerivedMethods () => GetMethods (); + } + + [ExpectedWarning ("IL2091")] + static void TestWithUnannotatedTypeArgument () + { + object a; + a = new DerivedWithMatchingAnnotation (); // IL2091 due to the instantiation + a = new DerivedWithNoAnnotations (); + } + + public static void Test () + { + Type t; + t = typeof (DerivedWithSpecificType); + t = typeof (DerivedWithMatchingAnnotation<>); + t = typeof (DerivedWithNoAnnotations<>); + t = typeof (DerivedWithMismatchAnnotation<>); + t = typeof (DerivedWithOneMismatch<>); + t = typeof (DerivedWithTwoMatching<,>); + + // Also try exact instantiations + object a; + a = new DerivedWithMatchingAnnotation (); + a = new DerivedWithMatchingAnnotation (); + + // Also try with unannotated type parameter + TestWithUnannotatedTypeArgument (); + + DerivedWithOnlyStaticMethodReference.GetDerivedMethods (); + } + } + + class TypeImplementingInterface + { + interface IWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> { } + + interface IWithPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> { } + + // No warning - annotations applied + class ImplementsWithSpecificType : IWithPublicMethods, IWithPublicFields { } + + // No warning - matching annotations + class ImplementsWithMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] TAll> + : IWithPublicMethods, IWithPublicFields + { } + + [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicFields))] + class ImplementsWithOneMismatch<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + : IWithPublicMethods, IWithPublicFields + { } + + [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicMethods))] + [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicFields))] + class ImplementsWithTwoMismatches< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + : IWithPublicMethods, IWithPublicFields + { } + + public static void Test () + { + // Instantiate the types + new ImplementsWithSpecificType (); + new ImplementsWithMatchingAnnotation (); + new ImplementsWithOneMismatch (); + new ImplementsWithTwoMismatches (); + + // Also reference the interfaces, otherwise they could be trimmed + Type t; + t = typeof (IWithPublicMethods<>); + t = typeof (IWithPublicFields<>); + } + } + + //.NativeAOT: Method parameter types are not interesting until something creates an instance of them + // so there's no need to validate generic arguments. See comment at the top of the file for more details. + class MethodParametersAndReturn + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + static void MethodWithSpecificType (TypeWithPublicMethods one, IWithTwo two) { } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MethodWithOneMismatch (TypeWithPublicMethods one) { } + + [ExpectedWarning ("IL2091", nameof (IWithTwo), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", nameof (TypeWithPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MethodWithTwoMismatches< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + (IWithTwo two, TypeWithPublicMethods one) + { } + + static TypeWithPublicMethods MethodWithSpecificReturnType () => null; + + static TypeWithPublicMethods MethodWithMatchingReturn<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () => null; + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static TypeWithPublicMethods MethodWithOneMismatchReturn () => null; + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static IWithTwo MethodWithTwoMismatchesInReturn< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () => null; + + class ConstructorWithOneMatchAndOneMismatch<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods> + { + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + public ConstructorWithOneMatchAndOneMismatch (IWithTwo two) { } + } + + public static void Test () + { + MethodWithSpecificType (null, null); + MethodWithOneMismatch (null); + MethodWithTwoMismatches (null, null); + + MethodWithSpecificReturnType (); + MethodWithMatchingReturn (); + MethodWithOneMismatchReturn (); + MethodWithTwoMismatchesInReturn (); + + _ = new ConstructorWithOneMatchAndOneMismatch (null); + } + } + + class MethodParametersAndReturnAccessedViaReflection + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + static void MethodWithSpecificType (TypeWithPublicMethods one, IWithTwo two) { } + + [ExpectedWarning ("IL2091")] + static void MethodWithOneMismatch (TypeWithPublicMethods one) { } + + [ExpectedWarning ("IL2091", nameof (IWithTwo))] + [ExpectedWarning ("IL2091", nameof (TypeWithPublicMethods))] + static void MethodWithTwoMismatches< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + (IWithTwo two, TypeWithPublicMethods one) + { } + + static TypeWithPublicMethods MethodWithSpecificReturnType () => null; + + static TypeWithPublicMethods MethodWithMatchingReturn<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () => null; + + [ExpectedWarning ("IL2091")] + static TypeWithPublicMethods MethodWithOneMismatchReturn () => null; + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static IWithTwo MethodWithTwoMismatchesInReturn< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () => null; + + class ConstructorWithOneMatchAndOneMismatch<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TMethods> + { + [ExpectedWarning ("IL2091")] + public ConstructorWithOneMatchAndOneMismatch (IWithTwo two) { } + } + + public static void Test () + { + // Access all of the methods via reflection + typeof (MethodParametersAndReturnAccessedViaReflection).RequiresNonPublicMethods (); + typeof (ConstructorWithOneMatchAndOneMismatch<>).RequiresPublicConstructors (); + } + } + + //.NativeAOT: Field types are not interesting until something creates an instance of them + // so there's no need to validate generic arguments. See comment at the top of the file for more details. + class FieldDefinition + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + class SpecificType + { + static TypeWithPublicMethods _field; + + public static void Test () + { + _field = null; + } + } + + class OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { + static TypeWithPublicMethods _field; + + public static void Test () + { + _field = null; + } + } + + class MultipleReferencesToTheSameType<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, TUnknown> + { + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static TypeWithPublicMethods _field1; + static TypeWithPublicMethods _field2; + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static TypeWithPublicMethods _field3; + + public static void Test () + { + _field1 = null; + _field2 = null; + _field3 = null; + } + } + + class TwoMismatchesInOne< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static IWithTwo _field; + + public static void Test () + { + _field = null; + } + } + + public static void Test () + { + SpecificType.Test (); + OneMatchingAnnotation.Test (); + MultipleReferencesToTheSameType.Test (); + TwoMismatchesInOne.Test (); + } + } + + class FieldDefinitionViaReflection + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + class SpecificType + { + static TypeWithPublicMethods _field; + } + + class OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { + static TypeWithPublicMethods _field; + } + + class MultipleReferencesToTheSameType<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, TUnknown> + { + [ExpectedWarning ("IL2091")] + static TypeWithPublicMethods _field1; + static TypeWithPublicMethods _field2; + [ExpectedWarning ("IL2091")] + static TypeWithPublicMethods _field3; + } + + class TwoMismatchesInOne< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static IWithTwo _field; + } + + public static void Test () + { + typeof (SpecificType).RequiresNonPublicFields (); + typeof (OneMatchingAnnotation<>).RequiresNonPublicFields (); + typeof (MultipleReferencesToTheSameType<,>).RequiresNonPublicFields (); + typeof (TwoMismatchesInOne<,>).RequiresNonPublicFields (); + } + } + + //.NativeAOT: Property types are not interesting until something creates an instance of them + // so there's no need to validate generic arguments. See comment at the top of the file for more details. + // In case of trimmer/aot it's even less important because properties don't exist in IL really + // and thus no code can manipulate them directly - only through reflection. + class PropertyDefinition + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { } + + class SpecificType + { + static TypeWithPublicMethods Property { get; set; } + + public static void Test () + { + Property = null; + } + } + + class OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + { + static TypeWithPublicMethods Property { get; set; } + + public static void Test () + { + Property = null; + } + } + + class MultipleReferencesToTheSameType<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, TUnknown> + { + // The warning is generated on the backing field + [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static TypeWithPublicMethods Property1 { + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + get; + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + set; + } + + static TypeWithPublicMethods Property2 { + get; + set; + } + + // The warning is generated on the backing field + [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static TypeWithPublicMethods Property3 { + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + get; + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + set; + } + + public static void Test () + { + Property1 = Property1; + Property2 = Property2; + Property3 = Property3; + } + } + + class TwoMismatchesInOne< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { + // The warnings are generated on the backing field + [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static IWithTwo Property { + // Getter is trimmed and doesn't produce any warning + get; + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + set; + } + + public static void Test () + { + Property = null; + } + } + + public static void Test () + { + SpecificType.Test (); + OneMatchingAnnotation.Test (); + MultipleReferencesToTheSameType.Test (); + TwoMismatchesInOne.Test (); + } + } + + class MethodBody + { + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> : Exception + { + public static void Method () { } + public void InstanceMethod () { } + + public static string Field; + public string InstanceField; + + public static string Property { get; set; } + } + + interface IWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + { + public static void Method () { } + public void InstanceMethod (); + + public static string Field; + + public static string Property { get; set; } + } + + class TypeWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> : Exception + { } + + static void MethodWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () { } + + void InstanceMethodWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () { } + + static void MethodWithTwo< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> () + { } + + static MethodBody GetInstance () => null; + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // return type // NativeAOT_StorageSpaceType + static TypeWithPublicMethods GetInstanceForTypeWithPublicMethods () => null; + + class TypeOf + { + static void AccessOpenTypeDefinition () + { + // Accessing the open type definition should not do anything on its own - just validating that it doesn't break anything + Type t = typeof (TypeWithPublicMethods<>); + t = typeof (IWithTwo<,>); + } + + static void SpecificType () + { + Type t = typeof (TypeWithPublicMethods); + t = typeof (IWithTwo); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + Type t = typeof (TypeWithPublicMethods); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameType< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + Type t = typeof (TypeWithPublicMethods); // Warn + t = typeof (TypeWithPublicMethods); // No warn + t = typeof (TypeWithPublicMethods); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + Type t = typeof (IWithTwo); + } + + public static void Test () + { + AccessOpenTypeDefinition (); + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameType (); + TwoMismatchesInOneStatement (); + } + } + + class MethodCallOnGenericMethod + { + static void SpecificType () + { + MethodWithPublicMethods (); + MethodWithTwo (); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + MethodWithPublicMethods (); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + MethodWithPublicMethods (); // Warn + MethodWithPublicMethods (); // No warn + MethodWithPublicMethods (); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + MethodWithTwo (); + } + + [ExpectedWarning ("IL2091")] + static void InstanceMethodMismatch () + { + GetInstance ().InstanceMethodWithPublicMethods (); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + InstanceMethodMismatch (); + } + } + + class MethodCallOnGenericType + { + static void SpecificType () + { + TypeWithPublicMethods.Method (); + IWithTwo.Method (); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + TypeWithPublicMethods.Method (); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + TypeWithPublicMethods.Method (); // Warn + TypeWithPublicMethods.Method (); // No warn + TypeWithPublicMethods.Method (); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + IWithTwo.Method (); + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // local variable // NativeAOT_StorageSpaceType + static void InstanceMethodMismatch () + { + TypeWithPublicMethods instance = GetInstanceForTypeWithPublicMethods (); + instance.InstanceMethod (); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + InstanceMethodMismatch (); + } + } + + class FieldAccessOnGenericType + { + static void SpecificType () + { + _ = TypeWithPublicMethods.Field; + IWithTwo.Field = ""; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + _ = TypeWithPublicMethods.Field; + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameField< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + _ = TypeWithPublicMethods.Field; // Warn + TypeWithPublicMethods.Field = ""; // No warn + TypeWithPublicMethods.Field = ""; // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + _ = IWithTwo.Field; + } + + // The local variable + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // access to the field // NativeAOT_StorageSpaceType + static void InstanceFieldMismatch () + { + TypeWithPublicMethods instance = GetInstanceForTypeWithPublicMethods (); + _ = instance.InstanceField; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameField (); + TwoMismatchesInOneStatement (); + InstanceFieldMismatch (); + } + } + + //.NativeAOT: Local variable types are not interesting until something creates an instance of them + // so there's no need to validate generic arguments. See comment at the top of the file for more details. + class LocalVariable + { + static void SpecificType () + { + TypeWithPublicMethods t = null; + IWithTwo i = null; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + TypeWithPublicMethods t = null; + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MultipleReferencesToTheSameType< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + TypeWithPublicMethods t1 = null; // Warn + TypeWithPublicMethods t2 = null; // No warn + TypeWithPublicMethods t3 = null; // Warn + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + IWithTwo i = null; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameType (); + TwoMismatchesInOneStatement (); + } + } + + class DelegateUsageOnGenericMethod + { + static void SpecificType () + { + var a = new Action (MethodWithPublicMethods); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + var a = new Action (MethodWithPublicMethods); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + var a1 = new Action (MethodWithPublicMethods); // Warn + var a2 = new Action (MethodWithPublicMethods); // No warn + var a3 = new Action (MethodWithPublicMethods); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + var a = new Action (MethodWithTwo); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class DelegateUsageOnGenericType + { + static void SpecificType () + { + var a = new Action (TypeWithPublicMethods.Method); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + var a = new Action (TypeWithPublicMethods.Method); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + var a1 = new Action (TypeWithPublicMethods.Method); // Warn + var a2 = new Action (TypeWithPublicMethods.Method); // No warn + var a3 = new Action (TypeWithPublicMethods.Method); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + var a = new Action (IWithTwo.Method); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class LdTokenOnGenericMethod + { + static void SpecificType () + { + Expression a = () => MethodWithPublicMethods (); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + Expression a = () => MethodWithPublicMethods (); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + Expression a = () => MethodWithPublicMethods (); // Warn + a = () => MethodWithPublicMethods (); // No warn + a = () => MethodWithPublicMethods (); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + Expression a = () => MethodWithTwo (); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class LdTokenOfMethodOnGenericType + { + static void SpecificType () + { + Expression a = () => TypeWithPublicMethods.Method (); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + Expression a = () => TypeWithPublicMethods.Method (); + } + + // There are two warnings per "callsite" in this case because the generated IL does + // ldtoken method + // ldtoken owningtype + // In order to call the right Expression APIs. + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + Expression a = () => TypeWithPublicMethods.Method (); // Warn + a = () => TypeWithPublicMethods.Method (); // No warn + a = () => TypeWithPublicMethods.Method (); // Warn + } + + // There are two warnings per "callsite" in this case because the generated IL does + // ldtoken method + // ldtoken owningtype + // In order to call the right Expression APIs. + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + Expression a = () => IWithTwo.Method (); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + class LdTokenOfFieldOnGenericType + { + static void SpecificType () + { + Expression> a = () => TypeWithPublicMethods.Field; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + Expression> a = () => TypeWithPublicMethods.Field; + } + + // There are two warnings per "callsite" in this case because the generated IL does + // ldtoken field + // ldtoken owningtype + // In order to call the right Expression APIs. + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void MultipleReferencesToTheSameField< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + Expression> a = () => TypeWithPublicMethods.Field; // Warn + a = () => TypeWithPublicMethods.Field; // No warn + a = () => TypeWithPublicMethods.Field; // Warn + } + + // There are two warnings per "callsite" in this case because the generated IL does + // ldtoken field + // ldtoken owningtype + // In order to call the right Expression APIs. + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + Expression> a = () => IWithTwo.Field; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameField (); + TwoMismatchesInOneStatement (); + } + } + + class LdTokenOfPropertyOnGenericType + { + static void SpecificType () + { + Expression> a = () => TypeWithPublicMethods.Property; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + Expression> a = () => TypeWithPublicMethods.Property; + } + + // There are two warnings per "callsite" in this case because the generated IL does + // ldtoken method (getter) + // ldtoken owningtype + // In order to call the right Expression APIs. + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void MultipleReferencesToTheSameProperty< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + Expression> a = () => TypeWithPublicMethods.Property; // Warn + a = () => TypeWithPublicMethods.Property; // No warn + a = () => TypeWithPublicMethods.Property; // Warn + } + + // There are two warnings per "callsite" in this case because the generated IL does + // ldtoken method (getter) + // ldtoken owningtype + // In order to call the right Expression APIs. + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + Expression> a = () => IWithTwo.Property; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameProperty (); + TwoMismatchesInOneStatement (); + } + } + + class CreateInstance + { + static void SpecificType () + { + object a = new TypeWithPublicMethods (); + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + object a = new TypeWithPublicMethods (); + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + object a1 = new TypeWithPublicMethods (); // Warn + object a2 = new TypeWithPublicMethods (); // No warn + object a3 = new TypeWithPublicMethods (); // Warn + } + + [ExpectedWarning ("IL2091")] + [ExpectedWarning ("IL2091")] + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + object a = new TypeWithTwo (); + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + //.NativeAOT: Checking an instance for its type is not interesting until something creates an instance of that type + // so there's no need to validate generic arguments. See comment at the top of the file for more details. + class IsInstance + { + static object _value = null; + + static void SpecificType () + { + bool a = _value is TypeWithPublicMethods; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + bool a = _value is TypeWithPublicMethods; + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + bool a1 = _value is TypeWithPublicMethods; // Warn + bool a2 = _value is TypeWithPublicMethods; // No warn + bool a3 = _value is TypeWithPublicMethods; // Warn + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + bool a = _value is TypeWithTwo; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + // This is basically the same operation as IsInstance + class AsType + { + static object _value = null; + + static void SpecificType () + { + object a = _value as TypeWithPublicMethods; + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + object a = _value as TypeWithPublicMethods; + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MultipleReferencesToTheSameMethod< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + object a1 = _value as TypeWithPublicMethods; // Warn + object a2 = _value as TypeWithPublicMethods; // No warn + object a3 = _value as TypeWithPublicMethods; // Warn + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + object a = _value as TypeWithTwo; + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameMethod (); + TwoMismatchesInOneStatement (); + } + } + + //.NativeAOT: Exception types are effectively very similar to local variable or method parameters. + // and are not interesting until something creates an instance of them + // so there's no need to validate generic arguments. See comment at the top of the file for more details. + class ExceptionCatch + { + static void SpecificType () + { + try { + DoNothing (); + } catch (TypeWithPublicMethods) { + } + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + try { + DoNothing (); + } catch (TypeWithPublicMethods) { + } + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MultipleReferencesToTheSameType< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + try { + DoNothing (); + } catch (TypeWithPublicMethods) { // Warn + } + + try { + DoNothing (); + } catch (TypeWithPublicMethods) { // No warn + } + + try { + DoNothing (); + } catch (TypeWithPublicMethods) { // Warn + } + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + try { + DoNothing (); + } catch (TypeWithTwo) { // Warn x2 + } + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameType (); + TwoMismatchesInOneStatement (); + } + } + + // This is basically the same as IsInstance and thus not dangerous + class ExceptionFilter + { + static void SpecificType () + { + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { + } + } + + static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { + } + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void MultipleReferencesToTheSameType< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + TUnknown> () + { + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { // Warn + } + + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { // No warn + } + + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithPublicMethods) { // Warn + } + } + + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType + static void TwoMismatchesInOneStatement< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + () + { + try { + DoNothing (); + } catch (Exception ex) when (ex is TypeWithTwo) { // Warn x2 + } + } + + public static void Test () + { + SpecificType (); + OneMatchingAnnotation (); + MultipleReferencesToTheSameType (); + TwoMismatchesInOneStatement (); + } + } + + public static void Test () + { + TypeOf.Test (); + MethodCallOnGenericMethod.Test (); + MethodCallOnGenericType.Test (); + FieldAccessOnGenericType.Test (); + LocalVariable.Test (); + DelegateUsageOnGenericMethod.Test (); + DelegateUsageOnGenericType.Test (); + LdTokenOnGenericMethod.Test (); + LdTokenOfMethodOnGenericType.Test (); + LdTokenOfFieldOnGenericType.Test (); + LdTokenOfPropertyOnGenericType.Test (); + CreateInstance.Test (); + IsInstance.Test (); + AsType.Test (); + ExceptionCatch.Test (); + ExceptionFilter.Test (); + } + } + + // There are no warnings due to data flow itself + // since the generic attributes must be fully instantiated always. + class GenericAttributes + { + class TypeWithPublicMethodsAttribute<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> + : Attribute + { } + + class TypeWithTwoAttribute< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> + : Attribute + { } + + [TypeWithPublicMethods] + static void OneSpecificType () { } + + [TypeWithTwo] + static void TwoSpecificTypes () { } + + public static void Test () + { + OneSpecificType (); + TwoSpecificTypes (); + } + } + + class NestedGenerics + { + class RequiresMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> + { + } + + class RequiresNothing + { + } + + class RequiresFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] T> + { + } + + static void GenericMethodNoRequirements () { } + + static void GenericMethodNoRequirementsAccessToT () + { + _ = typeof (T).Name; + } + + class GenericTypeWithMethodAndField + { + public void GenericInstanceMethod () { } + public static void GenericMethod () { } + + public static int Field; + } + + class TypeWithRUCMethod + { + [RequiresUnreferencedCode("--RUCMethod--")] + public static void RUCMethod () { } + } + + [ExpectedWarning ("IL2091", "TUnknown", "RequiresFields", nameof (DynamicallyAccessedMemberTypes.PublicFields))] + [ExpectedWarning ("IL2026", "--RUCMethod--")] + static void GenericMethodNesting () + { + GenericMethodNoRequirements>> (); // No warning + GenericMethodNoRequirements>>> (); // IL2091 + GenericMethodNoRequirements>>> (); // IL2026 + } + + [ExpectedWarning ("IL2091", "TUnknown", "RequiresFields", nameof (DynamicallyAccessedMemberTypes.PublicFields))] + [ExpectedWarning ("IL2026", "--RUCMethod--")] + static void GenericMethodNestingAccessToT () + { + GenericMethodNoRequirementsAccessToT>> (); // No warning + GenericMethodNoRequirementsAccessToT>>> (); // IL2091 + GenericMethodNoRequirementsAccessToT>>> (); // IL2026 + } + + [ExpectedWarning ("IL2091", "TUnknown", "RequiresFields", nameof (DynamicallyAccessedMemberTypes.PublicFields))] + [ExpectedWarning ("IL2026", "--RUCMethod--")] + static void GenericInstanceMethodNesting () + { + GenericTypeWithMethodAndField instance = new (); + instance.GenericInstanceMethod>> (); // No warning + instance.GenericInstanceMethod>>> (); // IL2091 + instance.GenericInstanceMethod>>> (); // IL2026 + } + + [ExpectedWarning ("IL2091", "TUnknown", "RequiresFields")] + [ExpectedWarning ("IL2091", "TUnknown", "RequiresMethods")] + [ExpectedWarning ("IL2026", "--RUCMethod--")] + [ExpectedWarning ("IL2026", "--RUCMethod--")] + static void MethodOnGenericTypeNesting () + { + GenericTypeWithMethodAndField>>.GenericMethod (); // No warning + GenericTypeWithMethodAndField>>> // IL2091 + .GenericMethod>>>> (); // IL2091 + GenericTypeWithMethodAndField>>> // IL2026 + .GenericMethod>>> (); // IL2026 + } + + [ExpectedWarning ("IL2091", "TUnknown", "RequiresFields", nameof (DynamicallyAccessedMemberTypes.PublicFields))] + [ExpectedWarning ("IL2026", "--RUCMethod--")] + static void FieldOnGenericTypeNesting () + { + GenericTypeWithMethodAndField>>.Field = 0; // No warning + _ = GenericTypeWithMethodAndField>>>.Field; // IL2091 + _ = GenericTypeWithMethodAndField>>>.Field; // IL2026 + } + + class BaseTypeGenericNesting + { + class Base + { + static Base () { _ = typeof (T).Name; } + } + + class DerivedWithNothing + : Base>> + { } + + [ExpectedWarning ("IL2091", "TUnknown", "RequiresFields", nameof (DynamicallyAccessedMemberTypes.PublicFields))] + class DerivedWithFields + : Base>>> + { + static DerivedWithFields() + { + } + } + + [ExpectedWarning ("IL2026", "--RUCMethod--")] + class DerivedWithRUC + : Base>>> + { } + + public static void Test() + { + Type a; + a = typeof(DerivedWithNothing); + a = typeof(DerivedWithFields); + a = typeof(DerivedWithRUC); + } + } + + class InterfaceGenericNesting + { + interface IBase + { + } + + class DerivedWithNothing + : IBase>> + { } + + [ExpectedWarning ("IL2091", "TUnknown", "RequiresFields", nameof (DynamicallyAccessedMemberTypes.PublicFields))] + class DerivedWithFields + : IBase>>> + { + static DerivedWithFields () + { + } + } + + [ExpectedWarning ("IL2026", "--RUCMethod--")] + class DerivedWithRUC + : IBase>>> + { } + + public static void Test () + { + Type a; + a = typeof (DerivedWithNothing); + a = typeof (DerivedWithFields); + a = typeof (DerivedWithRUC); + } + } + + public static void Test () + { + GenericMethodNesting (); + GenericMethodNestingAccessToT (); + GenericInstanceMethodNesting (); + MethodOnGenericTypeNesting (); + FieldOnGenericTypeNesting (); + BaseTypeGenericNesting.Test (); + InterfaceGenericNesting.Test (); + } + } + + class TestType { } + + static void DoNothing () { } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs index 2c4ef06302f04..bfc580234b2ef 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs @@ -3,10 +3,8 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; -using System.Reflection.Metadata.Ecma335; using System.Text; using FluentAssertions; using ILCompiler; @@ -25,42 +23,6 @@ namespace Mono.Linker.Tests.TestCasesRunner { public class AssemblyChecker { - internal readonly struct AssemblyQualifiedToken : IEquatable - { - public string? AssemblyName { get; } - public int Token { get; } - - public AssemblyQualifiedToken (string? assemblyName, int token) => (AssemblyName, Token) = (assemblyName, token); - - public AssemblyQualifiedToken (TypeSystemEntity entity) => - (AssemblyName, Token) = entity switch { - EcmaType type => (type.Module.Assembly.GetName ().Name, MetadataTokens.GetToken (type.Handle)), - EcmaMethod method => (method.Module.Assembly.GetName ().Name, MetadataTokens.GetToken (method.Handle)), - PropertyPseudoDesc property => (((EcmaType) property.OwningType).Module.Assembly.GetName ().Name, MetadataTokens.GetToken (property.Handle)), - EventPseudoDesc @event => (((EcmaType) @event.OwningType).Module.Assembly.GetName ().Name, MetadataTokens.GetToken (@event.Handle)), - ILStubMethod => (null, 0), // Ignore compiler generated methods - _ => throw new NotSupportedException ($"The infra doesn't support getting a token for {entity} yet.") - }; - - public AssemblyQualifiedToken (IMemberDefinition member) => - (AssemblyName, Token) = member switch { - TypeDefinition type => (type.Module.Assembly.Name.Name, type.MetadataToken.ToInt32 ()), - MethodDefinition method => (method.Module.Assembly.Name.Name, method.MetadataToken.ToInt32 ()), - PropertyDefinition property => (property.Module.Assembly.Name.Name, property.MetadataToken.ToInt32 ()), - EventDefinition @event => (@event.Module.Assembly.Name.Name, @event.MetadataToken.ToInt32 ()), - FieldDefinition field => (field.Module.Assembly.Name.Name, field.MetadataToken.ToInt32 ()), - _ => throw new NotSupportedException ($"The infra doesn't support getting a token for {member} yet.") - }; - - public override int GetHashCode () => AssemblyName == null ? 0 : AssemblyName.GetHashCode () ^ Token.GetHashCode (); - public override string ToString () => $"{AssemblyName}: {Token}"; - public bool Equals (AssemblyQualifiedToken other) => - string.CompareOrdinal (AssemblyName, other.AssemblyName) == 0 && Token == other.Token; - public override bool Equals ([NotNullWhen (true)] object? obj) => ((AssemblyQualifiedToken?) obj)?.Equals (this) == true; - - public bool IsNil => AssemblyName == null; - } - private readonly BaseAssemblyResolver originalsResolver; private readonly ReaderParameters originalReaderParameters; private readonly AssemblyDefinition originalAssembly; diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyQualifiedToken.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyQualifiedToken.cs new file mode 100644 index 0000000000000..af39d8d687883 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyQualifiedToken.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Metadata.Ecma335; +using ILCompiler; +using Internal.IL.Stubs; +using Internal.TypeSystem.Ecma; +using Internal.TypeSystem; +using Mono.Cecil; + +namespace Mono.Linker.Tests.TestCasesRunner +{ + internal readonly struct AssemblyQualifiedToken : IEquatable + { + public string? AssemblyName { get; } + public int Token { get; } + + public AssemblyQualifiedToken (string? assemblyName, int token) => (AssemblyName, Token) = (assemblyName, token); + + public AssemblyQualifiedToken (TypeSystemEntity entity) => + (AssemblyName, Token) = entity switch { + EcmaType type => (type.Module.Assembly.GetName ().Name, MetadataTokens.GetToken (type.Handle)), + EcmaMethod method => (method.Module.Assembly.GetName ().Name, MetadataTokens.GetToken (method.Handle)), + EcmaField field => (field.Module.Assembly.GetName ().Name, MetadataTokens.GetToken (field.Handle)), + PropertyPseudoDesc property => (((EcmaType) property.OwningType).Module.Assembly.GetName ().Name, MetadataTokens.GetToken (property.Handle)), + EventPseudoDesc @event => (((EcmaType) @event.OwningType).Module.Assembly.GetName ().Name, MetadataTokens.GetToken (@event.Handle)), + ILStubMethod => (null, 0), // Ignore compiler generated methods + _ => throw new NotSupportedException ($"The infra doesn't support getting a token for {entity} yet.") + }; + + public AssemblyQualifiedToken (IMemberDefinition member) => + (AssemblyName, Token) = member switch { + TypeDefinition type => (type.Module.Assembly.Name.Name, type.MetadataToken.ToInt32 ()), + MethodDefinition method => (method.Module.Assembly.Name.Name, method.MetadataToken.ToInt32 ()), + PropertyDefinition property => (property.Module.Assembly.Name.Name, property.MetadataToken.ToInt32 ()), + EventDefinition @event => (@event.Module.Assembly.Name.Name, @event.MetadataToken.ToInt32 ()), + FieldDefinition field => (field.Module.Assembly.Name.Name, field.MetadataToken.ToInt32 ()), + _ => throw new NotSupportedException ($"The infra doesn't support getting a token for {member} yet.") + }; + + public override int GetHashCode () => AssemblyName == null ? 0 : AssemblyName.GetHashCode () ^ Token.GetHashCode (); + public override string ToString () => $"{AssemblyName}: {Token}"; + public bool Equals (AssemblyQualifiedToken other) => + string.CompareOrdinal (AssemblyName, other.AssemblyName) == 0 && Token == other.Token; + public override bool Equals ([NotNullWhen (true)] object? obj) => ((AssemblyQualifiedToken?) obj)?.Equals (this) == true; + + public bool IsNil => AssemblyName == null; + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs index 090a3a1b039b9..b0c0bdd4cfafb 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -398,10 +398,17 @@ static bool LogMessageHasSameOriginMember (MessageContainer mc, ICustomAttribute { var origin = mc.Origin; Debug.Assert (origin != null); - if (NameUtils.GetActualOriginDisplayName (origin?.MemberDefinition) == NameUtils.GetExpectedOriginDisplayName (expectedOriginProvider)) + if (origin?.MemberDefinition == null) + return false; + if (expectedOriginProvider is not IMemberDefinition expectedOriginMember) + return false; + + var actualOriginToken = new AssemblyQualifiedToken (origin.Value.MemberDefinition); + var expectedOriginToken = new AssemblyQualifiedToken (expectedOriginMember); + if (actualOriginToken.Equals(expectedOriginToken)) return true; - var actualMember = origin!.Value.MemberDefinition; + var actualMember = origin.Value.MemberDefinition; // Compensate for cases where for some reason the OM doesn't preserve the declaring types // on certain things after trimming. if (actualMember != null && GetOwningType (actualMember) == null && diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DependencyInjectionPattern.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DependencyInjectionPattern.cs new file mode 100644 index 0000000000000..0c88b5e2b2186 --- /dev/null +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/DependencyInjectionPattern.cs @@ -0,0 +1,177 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +public class DependencyInjectionPattern +{ + public static int Run() + { + Services services = new(); + services.RegisterService(typeof(INameProvider<>), typeof(NameProviderService<>)); + services.RegisterService(typeof(IDataObjectPrinter), typeof(DataObjectPrinterService)); + + var printer = services.GetService(); + var actual = printer.GetNameLength(new DataObject() { Name = "0123456789" }); + Assert.Equal(10, actual); + + services.RegisterService(typeof(ICustomFactory<>), typeof(CustomFactoryImpl<>)); + + var customFactory = services.GetService>(); + var created = customFactory.Create(); + Assert.Equal(42, created.GetValue()); + + return 100; + } +} + +public class DataObject +{ + public string Name { get; set; } +} + +// Simplistic implementation of DI which is comparable in behavior to our DI +class Services +{ + private Dictionary _services = new Dictionary(); + + public void RegisterService(Type interfaceType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementationType) + { + _services.Add(interfaceType, implementationType); + } + + public T GetService() + { + return (T)GetService(typeof(T)); + } + + public object GetService(Type interfaceType) + { + Type typeDef = interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType; + Type implementationType = GetImplementationType(typeDef); + + if (implementationType.IsGenericTypeDefinition) + { + for (int i = 0; i < implementationType.GetGenericArguments().Length; i++) + { + Type genericArgument = implementationType.GetGenericArguments()[i]; + Type genericParameter = interfaceType.GetGenericArguments()[i]; + + // Validate that DAM annotations match + if (!DamAnnotationsMatch(genericArgument, genericParameter)) + throw new InvalidOperationException(); + + if (genericParameter.IsValueType) + throw new InvalidOperationException(); + } + + implementationType = InstantiateServiceType(implementationType, interfaceType.GetGenericArguments()); + } + + ConstructorInfo constructor = implementationType.GetConstructors()[0]; // Simplification + if (constructor.GetParameters().Length > 0) + { + List instances = new(); + foreach (var parameter in constructor.GetParameters()) + { + instances.Add(GetService(parameter.ParameterType)); + } + + return Activator.CreateInstance(implementationType, instances.ToArray())!; + } + else + { + return Activator.CreateInstance(implementationType)!; + } + + [UnconditionalSuppressMessage("", "IL2068", Justification = "We only add types with the right annotation to the dictionary")] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + Type GetImplementationType(Type interfaceType) + { + if (!_services.TryGetValue(interfaceType, out Type? implementationType)) + throw new NotImplementedException(); + + return implementationType; + } + + [UnconditionalSuppressMessage("", "IL2055", Justification = "We validated that the type parameters match - THIS IS WRONG")] + [UnconditionalSuppressMessage("", "IL3050", Justification = "We validated there are no value types")] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + Type InstantiateServiceType([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type typeDef, Type[] typeParameters) + { + return typeDef.MakeGenericType(typeParameters); + } + } + + private bool DamAnnotationsMatch(Type argument, Type parameter) + { + // .... - not interesting for this test, it will be true in the cases we use in this test + return true; + } +} + +interface INameProvider<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> +{ + string? GetName(T instance); +} + +class NameProviderService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> + : INameProvider +{ + public string? GetName(T instance) + { + return (string?)typeof(T).GetProperty("Name")?.GetValue(instance); + } +} + +interface IDataObjectPrinter +{ + int GetNameLength(DataObject instance); +} + +class DataObjectPrinterService : IDataObjectPrinter +{ + // The data flow is not applied on the INameProvider here, or in the method parameter + // or in the call to the GetName below inside Print. + INameProvider _nameProvider; + + public DataObjectPrinterService(INameProvider nameProvider) + { + _nameProvider = nameProvider; + } + + public int GetNameLength(DataObject instance) + { + // This throws because DataObject.Name is not preserved + string? name = _nameProvider.GetName(instance); + return name == null ? 0 : name.Length; + } +} + +interface ICustomFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T> +{ + T Create(); +} + +class CustomFactoryImpl<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T> : ICustomFactory +{ + public T Create() + { + return Activator.CreateInstance(); + } +} + +class FactoryCreated +{ + int _value; + + public FactoryCreated() + { + _value = 42; + } + + public int GetValue() => _value; +} \ No newline at end of file diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Main.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Main.cs index e2c96e8cc0029..7a46f41961da1 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Main.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Main.cs @@ -8,6 +8,7 @@ success &= RunTest(DeadCodeElimination.Run); success &= RunTest(FeatureSwitches.Run); success &= RunTest(ILLinkDescriptor.Run); +success &= RunTest(DependencyInjectionPattern.Run); return success ? 100 : 1; diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj index 799e3a9afa2fd..f8bc1ef93f329 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/TrimmingBehaviors.csproj @@ -12,6 +12,7 @@ +