diff --git a/src/coreclr/tools/aot/.editorconfig b/src/coreclr/tools/aot/.editorconfig new file mode 100644 index 0000000000000..5f1bbfd242997 --- /dev/null +++ b/src/coreclr/tools/aot/.editorconfig @@ -0,0 +1,29 @@ +# Linker port settings: +# A newline ending every file +# Use tabs as indentation +[ILLink.Shared/**.cs] +indent_style = tab +indent_size = 4 +csharp_new_line_before_open_brace = types,methods +csharp_new_line_before_else = false +csharp_new_line_before_catch = false +csharp_new_line_before_finally = false +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_declaration_name_and_open_parenthesis = true +csharp_space_between_method_call_name_and_opening_parenthesis = true +csharp_space_before_open_square_brackets = false +csharp_space_after_cast = true + +csharp_indent_switch_labels = false + +# Sort using and Import directives with System.* appearing first +dotnet_sort_system_directives_first = true + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs index a4e3ee1ae0df1..dc1bf6c9cd0c4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs @@ -71,7 +71,7 @@ public void TestDependencyGraphInvariants(EcmaMethod method) UsageBasedMetadataManager metadataManager = new UsageBasedMetadataManager(compilationGroup, context, new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(), null, new NoStackTraceEmissionPolicy(), new NoDynamicInvokeThunkGenerationPolicy(), - new Dataflow.FlowAnnotations(Logger.Null, ilProvider), UsageBasedMetadataGenerationOptions.None, + new ILLink.Shared.TrimAnalysis.FlowAnnotations(Logger.Null, ilProvider), UsageBasedMetadataGenerationOptions.None, Logger.Null, Array.Empty>(), Array.Empty(), Array.Empty()); CompilationBuilder builder = new RyuJitCompilationBuilder(context, compilationGroup) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ArrayValue.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ArrayValue.cs new file mode 100644 index 0000000000000..5c48191c8eca6 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ArrayValue.cs @@ -0,0 +1,130 @@ +// 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.Text; +using ILCompiler.Dataflow; +using ILLink.Shared.DataFlow; +using Internal.TypeSystem; + +using MultiValue = ILLink.Shared.DataFlow.ValueSet; + +#nullable enable + +namespace ILLink.Shared.TrimAnalysis +{ + partial record ArrayValue + { + public static MultiValue Create(MultiValue size, TypeDesc elementType) + { + MultiValue result = MultiValueLattice.Top; + foreach (var sizeValue in size) + { + result = MultiValueLattice.Meet(result, new MultiValue(new ArrayValue(sizeValue, elementType))); + } + + return result; + } + + public static MultiValue Create(int size, TypeDesc elementType) + { + return new MultiValue(new ArrayValue(new ConstIntValue(size), elementType)); + } + + /// + /// Constructs an array value of the given size + /// + ArrayValue(SingleValue size, TypeDesc elementType) + { + Size = size; + ElementType = elementType; + IndexValues = new Dictionary(); + } + + public TypeDesc ElementType { get; } + public Dictionary IndexValues { get; } + + public partial bool TryGetValueByIndex(int index, out MultiValue value) + { + if (IndexValues.TryGetValue(index, out var valuePair)) + { + value = valuePair.Value; + return true; + } + + value = default; + return false; + } + + public override int GetHashCode() + { + return HashCode.Combine(GetType().GetHashCode(), Size); + } + + public bool Equals(ArrayValue? otherArr) + { + if (otherArr == null) + return false; + + bool equals = Size.Equals(otherArr.Size); + equals &= IndexValues.Count == otherArr.IndexValues.Count; + if (!equals) + return false; + + // If both sets T and O are the same size and "T intersect O" is empty, then T == O. + HashSet> thisValueSet = new(IndexValues); + HashSet> otherValueSet = new(otherArr.IndexValues); + thisValueSet.ExceptWith(otherValueSet); + return thisValueSet.Count == 0; + } + + public override SingleValue DeepCopy() + { + var newValue = new ArrayValue(Size.DeepCopy(), ElementType); + foreach (var kvp in IndexValues) + { + newValue.IndexValues.Add(kvp.Key, new ValueBasicBlockPair(kvp.Value.Value.Clone(), kvp.Value.BasicBlockIndex)); + } + + return newValue; + } + + public override string ToString() + { + StringBuilder result = new(); + result.Append("Array Size:"); + result.Append(this.ValueToString(Size)); + + result.Append(", Values:("); + bool first = true; + foreach (var element in IndexValues) + { + if (!first) + { + result.Append(","); + first = false; + } + + result.Append("("); + result.Append(element.Key); + result.Append(",("); + bool firstValue = true; + foreach (var v in element.Value.Value) + { + if (firstValue) + { + result.Append(","); + firstValue = false; + } + + result.Append(v.ToString()); + } + result.Append("))"); + } + result.Append(')'); + + return result.ToString(); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticContext.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticContext.cs new file mode 100644 index 0000000000000..afb38e0370a33 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticContext.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using ILCompiler; +using ILCompiler.Logging; + +#nullable enable + +namespace ILLink.Shared.TrimAnalysis +{ + readonly partial struct DiagnosticContext + { + public readonly MessageOrigin Origin; + public readonly bool DiagnosticsEnabled; + readonly Logger _logger; + + public DiagnosticContext(in MessageOrigin origin, bool diagnosticsEnabled, Logger logger) + => (Origin, DiagnosticsEnabled, _logger) = (origin, diagnosticsEnabled, logger); + + public partial void AddDiagnostic(DiagnosticId id, params string[] args) + { + if (DiagnosticsEnabled) + _logger.LogWarning(Origin, id, args); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs index 828c073f24687..5cf498bcfc69b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs @@ -131,12 +131,12 @@ internal static bool IsInRequiresScope(this MethodDesc method, string requiresAt method.IsInRequiresScope(requiresAttribute, true); /// - /// True if member of a call is considered to be annotated with the Requires... attribute. - /// Doesn't check the associated symbol for overrides and virtual methods because we should warn on mismatched between the property AND the accessors - /// - /// - /// MethodDesc that is either an overriding member or an overriden/virtual member - /// + /// True if member of a call is considered to be annotated with the Requires... attribute. + /// Doesn't check the associated symbol for overrides and virtual methods because we should warn on mismatched between the property AND the accessors + /// + /// + /// MethodDesc that is either an overriding member or an overriden/virtual member + /// internal static bool IsOverrideInRequiresScope(this MethodDesc method, string requiresAttribute) => method.IsInRequiresScope(requiresAttribute, false); @@ -185,10 +185,10 @@ internal static bool DoesPropertyRequire(this PropertyPseudoDesc property, strin TryGetRequiresAttribute(property, requiresAttribute, out attribute); /// - /// Determines if member requires (and thus any usage of such method should be warned about). - /// - /// Unlike only static methods - /// and .ctors are reported as requires when the declaring type has Requires on it. + /// Determines if member requires (and thus any usage of such method should be warned about). + /// + /// Unlike only static methods + /// and .ctors are reported as requires when the declaring type has Requires on it. internal static bool DoesMemberRequire(this TypeSystemEntity member, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue? attribute) { attribute = null; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DynamicallyAccessedMembersBinder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DynamicallyAccessedMembersBinder.cs index 1704c3c826794..4b621432c7495 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DynamicallyAccessedMembersBinder.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DynamicallyAccessedMembersBinder.cs @@ -352,7 +352,7 @@ public static IEnumerable GetEventsOnTypeHierarchy(this TypeDes type = type.BaseType; onBaseType = true; } - + while (type != null) { if (type.GetTypeDefinition() is not EcmaType ecmaType) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/EcmaExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/EcmaExtensions.cs index 07dcfb8362cdb..77266e84ddca4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/EcmaExtensions.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/EcmaExtensions.cs @@ -22,7 +22,7 @@ public static bool IsPublic(this MethodDesc method) public static bool IsPublic(this FieldDesc field) { return field.GetTypicalFieldDefinition() is EcmaField ecmaField - && (ecmaField.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public; + && (ecmaField.Attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public; } public static bool IsPrivate(this MethodDesc method) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FieldValue.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FieldValue.cs new file mode 100644 index 0000000000000..3c23fba55f5f3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FieldValue.cs @@ -0,0 +1,41 @@ +// 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.CodeAnalysis; +using ILCompiler; +using ILCompiler.Dataflow; +using ILLink.Shared.DataFlow; +using Internal.TypeSystem; + +#nullable enable + +namespace ILLink.Shared.TrimAnalysis +{ + + /// + /// A representation of a field. Typically a result of ldfld. + /// + sealed partial record FieldValue : IValueWithStaticType + { + public FieldValue(FieldDesc field, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + StaticType = field.FieldType; + Field = field; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public readonly FieldDesc Field; + + public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } + + public override IEnumerable GetDiagnosticArgumentsForAnnotationMismatch() + => new string[] { Field.GetDisplayName() }; + + public TypeDesc? StaticType { get; } + + public override SingleValue DeepCopy() => this; // This value is immutable + + public override string ToString() => this.ValueToString(Field, DynamicallyAccessedMemberTypes); + } +} 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 107a15ea48199..489c2726eb278 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs @@ -9,17 +9,22 @@ using Internal.IL; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; - +using ILCompiler; +using ILCompiler.Dataflow; using ILLink.Shared; +using ILLink.Shared.TypeSystemProxy; using Debug = System.Diagnostics.Debug; +using WellKnownType = Internal.TypeSystem.WellKnownType; + +#nullable enable -namespace ILCompiler.Dataflow +namespace ILLink.Shared.TrimAnalysis { /// /// Caches dataflow annotations for type members. /// - public class FlowAnnotations + public partial class FlowAnnotations { private readonly TypeAnnotationsHashtable _hashtable; private readonly Logger _logger; @@ -145,7 +150,7 @@ public DynamicallyAccessedMemberTypes GetGenericParameterAnnotation(GenericParam public bool ShouldWarnWhenAccessedForReflection(MethodDesc method) { method = method.GetTypicalMethodDefinition(); - + if (!GetAnnotations(method.OwningType).TryGetAnnotation(method, out var annotation)) return false; @@ -249,7 +254,7 @@ protected override TypeAnnotations CreateValueFromKey(TypeDesc key) // class, interface, struct can have annotations TypeDefinition typeDef = reader.GetTypeDefinition(ecmaType.Handle); DynamicallyAccessedMemberTypes typeAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, typeDef.GetCustomAttributes()); - + try { // Also inherit annotation from bases @@ -301,14 +306,14 @@ protected override TypeAnnotations CreateValueFromKey(TypeDesc key) // Next go over all methods with an explicit annotation foreach (EcmaMethod method in ecmaType.GetMethods()) { - DynamicallyAccessedMemberTypes[] paramAnnotations = null; + DynamicallyAccessedMemberTypes[]? paramAnnotations = null; // We convert indices from metadata space to IL space here. // IL space assigns index 0 to the `this` parameter on instance methods. DynamicallyAccessedMemberTypes methodMemberTypes = GetMemberTypesForDynamicallyAccessedMembersAttribute(reader, reader.GetMethodDefinition(method.Handle).GetCustomAttributes()); - + MethodSignature signature; try { @@ -383,7 +388,7 @@ protected override TypeAnnotations CreateValueFromKey(TypeDesc key) } } - DynamicallyAccessedMemberTypes[] genericParameterAnnotations = null; + DynamicallyAccessedMemberTypes[]? genericParameterAnnotations = null; foreach (EcmaGenericParameter genericParameter in method.Instantiation) { GenericParameter genericParameterDef = reader.GetGenericParameter(genericParameter.Handle); @@ -431,7 +436,7 @@ protected override TypeAnnotations CreateValueFromKey(TypeDesc key) continue; } - FieldDesc backingFieldFromSetter = null; + FieldDesc? backingFieldFromSetter = null; // Propagate the annotation to the setter method MethodDesc setMethod = property.SetMethod; @@ -451,7 +456,7 @@ protected override TypeAnnotations CreateValueFromKey(TypeDesc key) if (annotatedMethods.Any(a => a.Method == setMethod)) { - + _logger.LogWarning(setMethod, DiagnosticId.DynamicallyAccessedMembersConflictsBetweenPropertyAndAccessor, property.GetDisplayName(), setMethod.GetDisplayName()); } else @@ -466,7 +471,7 @@ protected override TypeAnnotations CreateValueFromKey(TypeDesc key) } } - FieldDesc backingFieldFromGetter = null; + FieldDesc? backingFieldFromGetter = null; // Propagate the annotation to the getter method MethodDesc getMethod = property.GetMethod; @@ -494,7 +499,7 @@ protected override TypeAnnotations CreateValueFromKey(TypeDesc key) } } - FieldDesc backingField; + FieldDesc? backingField; if (backingFieldFromGetter != null && backingFieldFromSetter != null && backingFieldFromGetter != backingFieldFromSetter) { @@ -519,7 +524,7 @@ protected override TypeAnnotations CreateValueFromKey(TypeDesc key) } } - DynamicallyAccessedMemberTypes[] typeGenericParameterAnnotations = null; + DynamicallyAccessedMemberTypes[]? typeGenericParameterAnnotations = null; foreach (EcmaGenericParameter genericParameter in ecmaType.Instantiation) { GenericParameter genericParameterDef = reader.GetGenericParameter(genericParameter.Handle); @@ -536,7 +541,7 @@ protected override TypeAnnotations CreateValueFromKey(TypeDesc key) return new TypeAnnotations(ecmaType, typeAnnotation, annotatedMethods.ToArray(), annotatedFields.ToArray(), typeGenericParameterAnnotations); } - private static bool ScanMethodBodyForFieldAccess(MethodIL body, bool write, out FieldDesc found) + private static bool ScanMethodBodyForFieldAccess(MethodIL body, bool write, out FieldDesc? found) { // Tries to find the backing field for a property getter/setter. // Returns true if this is a method body that we can unambiguously analyze. @@ -637,7 +642,7 @@ internal void ValidateMethodAnnotationsAreSame(MethodDesc method, MethodDesc bas if (methodAnnotations.ParameterAnnotations != null || baseMethodAnnotations.ParameterAnnotations != null) { if (methodAnnotations.ParameterAnnotations == null) - ValidateMethodParametersHaveNoAnnotations(baseMethodAnnotations.ParameterAnnotations, method, baseMethod, method); + ValidateMethodParametersHaveNoAnnotations(baseMethodAnnotations.ParameterAnnotations!, method, baseMethod, method); else if (baseMethodAnnotations.ParameterAnnotations == null) ValidateMethodParametersHaveNoAnnotations(methodAnnotations.ParameterAnnotations, method, baseMethod, method); else @@ -659,7 +664,7 @@ internal void ValidateMethodAnnotationsAreSame(MethodDesc method, MethodDesc bas if (methodAnnotations.GenericParameterAnnotations != null || baseMethodAnnotations.GenericParameterAnnotations != null) { if (methodAnnotations.GenericParameterAnnotations == null) - ValidateMethodGenericParametersHaveNoAnnotations(baseMethodAnnotations.GenericParameterAnnotations, method, baseMethod, method); + ValidateMethodGenericParametersHaveNoAnnotations(baseMethodAnnotations.GenericParameterAnnotations!, method, baseMethod, method); else if (baseMethodAnnotations.GenericParameterAnnotations == null) ValidateMethodGenericParametersHaveNoAnnotations(methodAnnotations.GenericParameterAnnotations, method, baseMethod, method); else @@ -740,18 +745,18 @@ private class TypeAnnotations { public readonly TypeDesc Type; public readonly DynamicallyAccessedMemberTypes TypeAnnotation; - private readonly MethodAnnotations[] _annotatedMethods; - private readonly FieldAnnotation[] _annotatedFields; - private readonly DynamicallyAccessedMemberTypes[] _genericParameterAnnotations; + private readonly MethodAnnotations[]? _annotatedMethods; + private readonly FieldAnnotation[]? _annotatedFields; + private readonly DynamicallyAccessedMemberTypes[]? _genericParameterAnnotations; public bool IsDefault => _annotatedMethods == null && _annotatedFields == null && _genericParameterAnnotations == null; public TypeAnnotations( TypeDesc type, DynamicallyAccessedMemberTypes typeAnnotations, - MethodAnnotations[] annotatedMethods, - FieldAnnotation[] annotatedFields, - DynamicallyAccessedMemberTypes[] genericParameterAnnotations) + MethodAnnotations[]? annotatedMethods, + FieldAnnotation[]? annotatedFields, + DynamicallyAccessedMemberTypes[]? genericParameterAnnotations) => (Type, TypeAnnotation, _annotatedMethods, _annotatedFields, _genericParameterAnnotations) = (type, typeAnnotations, annotatedMethods, annotatedFields, genericParameterAnnotations); @@ -820,15 +825,15 @@ public bool TryGetAnnotation(GenericParameterDesc genericParameter, out Dynamica private readonly struct MethodAnnotations { public readonly MethodDesc Method; - public readonly DynamicallyAccessedMemberTypes[] ParameterAnnotations; + public readonly DynamicallyAccessedMemberTypes[]? ParameterAnnotations; public readonly DynamicallyAccessedMemberTypes ReturnParameterAnnotation; - public readonly DynamicallyAccessedMemberTypes[] GenericParameterAnnotations; + public readonly DynamicallyAccessedMemberTypes[]? GenericParameterAnnotations; public MethodAnnotations( MethodDesc method, - DynamicallyAccessedMemberTypes[] paramAnnotations, + DynamicallyAccessedMemberTypes[]? paramAnnotations, DynamicallyAccessedMemberTypes returnParamAnnotations, - DynamicallyAccessedMemberTypes[] genericParameterAnnotations) + DynamicallyAccessedMemberTypes[]? genericParameterAnnotations) => (Method, ParameterAnnotations, ReturnParameterAnnotation, GenericParameterAnnotations) = (method, paramAnnotations, returnParamAnnotations, genericParameterAnnotations); @@ -860,5 +865,31 @@ private readonly struct FieldAnnotation public FieldAnnotation(FieldDesc field, DynamicallyAccessedMemberTypes annotation) => (Field, Annotation) = (field, annotation); } + + internal partial bool MethodRequiresDataFlowAnalysis(MethodProxy method) + => RequiresDataflowAnalysis(method.Method); + + internal partial MethodReturnValue GetMethodReturnValue(MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => new MethodReturnValue(method.Method, dynamicallyAccessedMemberTypes); + + internal partial MethodReturnValue GetMethodReturnValue(MethodProxy method) + => GetMethodReturnValue(method, GetReturnParameterAnnotation(method.Method)); + + internal partial GenericParameterValue GetGenericParameterValue(GenericParameterProxy genericParameter) + => new GenericParameterValue(genericParameter.GenericParameter, GetGenericParameterAnnotation(genericParameter.GenericParameter)); + +#pragma warning disable CA1822 // Mark members as static - keep this an instance method for consistency with the others + internal partial MethodThisParameterValue GetMethodThisParameterValue(MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => new MethodThisParameterValue(method.Method, dynamicallyAccessedMemberTypes); +#pragma warning restore CA1822 + + internal partial MethodThisParameterValue GetMethodThisParameterValue(MethodProxy method) + => GetMethodThisParameterValue(method, GetParameterAnnotation(method.Method, 0)); + + internal partial MethodParameterValue GetMethodParameterValue(MethodProxy method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => new(method.Method, parameterIndex, dynamicallyAccessedMemberTypes); + + internal partial MethodParameterValue GetMethodParameterValue(MethodProxy method, int parameterIndex) + => GetMethodParameterValue(method, parameterIndex, GetParameterAnnotation(method.Method, parameterIndex + (method.IsStatic() ? 0 : 1))); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericParameterProxy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericParameterProxy.cs new file mode 100644 index 0000000000000..b04e245ee50e5 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericParameterProxy.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +#nullable enable + +namespace ILLink.Shared.TypeSystemProxy +{ + internal readonly partial struct GenericParameterProxy + { + public GenericParameterProxy(GenericParameterDesc genericParameter) => GenericParameter = genericParameter; + + public static implicit operator GenericParameterProxy(GenericParameterDesc genericParameter) => new(genericParameter); + + internal partial bool HasDefaultConstructorConstraint() => GenericParameter.HasDefaultConstructorConstraint; + + public readonly GenericParameterDesc GenericParameter; + + public override string ToString() => GenericParameter.ToString(); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericParameterValue.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericParameterValue.cs new file mode 100644 index 0000000000000..423b17c54d5f1 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericParameterValue.cs @@ -0,0 +1,35 @@ +// 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.CodeAnalysis; +using ILCompiler.Dataflow; +using ILLink.Shared.DataFlow; +using Internal.TypeSystem; + +#nullable enable + +namespace ILLink.Shared.TrimAnalysis +{ + /// + /// This is a System.Type value which represents generic parameter (basically result of typeof(T)) + /// Its actual type is unknown, but it can have annotations. + /// + partial record GenericParameterValue + { + public GenericParameterValue(GenericParameterDesc genericParameter, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + GenericParameter = new(genericParameter); + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } + + public override IEnumerable GetDiagnosticArgumentsForAnnotationMismatch() + => new string[] { GenericParameter.GenericParameter.Name, DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(new GenericParameterOrigin(GenericParameter.GenericParameter)) }; + + public override SingleValue DeepCopy() => this; // This value is immutable + + public override string ToString() => this.ValueToString(GenericParameter, DynamicallyAccessedMemberTypes); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs new file mode 100644 index 0000000000000..9b41163b33ad4 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs @@ -0,0 +1,126 @@ +// 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.Reflection; +using ILCompiler; +using ILCompiler.Dataflow; +using ILLink.Shared.TypeSystemProxy; +using Internal.TypeSystem; + +using WellKnownType = ILLink.Shared.TypeSystemProxy.WellKnownType; + +#nullable enable + +namespace ILLink.Shared.TrimAnalysis +{ + partial struct HandleCallAction + { +#pragma warning disable CA1822 // Mark members as static - the other partial implementations might need to be instance methods + + readonly ReflectionMarker _reflectionMarker; + readonly MethodDesc _callingMethod; + readonly Origin _memberWithRequirements; + + public HandleCallAction( + FlowAnnotations annotations, + ReflectionMarker reflectionMarker, + in DiagnosticContext diagnosticContext, + MethodDesc callingMethod, + Origin memberWithRequirements) + { + _reflectionMarker = reflectionMarker; + _diagnosticContext = diagnosticContext; + _callingMethod = callingMethod; + _annotations = annotations; + _memberWithRequirements = memberWithRequirements; + _requireDynamicallyAccessedMembersAction = new(reflectionMarker, diagnosticContext, memberWithRequirements); + } + + private partial bool MethodIsTypeConstructor(MethodProxy method) + { + if (!method.Method.IsConstructor) + return false; + TypeDesc? type = method.Method.OwningType; + while (type is not null) + { + if (type.IsTypeOf(WellKnownType.System_Type)) + return true; + type = type.BaseType; + } + return false; + } + + private partial IEnumerable GetMethodsOnTypeHierarchy(TypeProxy type, string name, BindingFlags? bindingFlags) + { + foreach (var method in type.Type.GetMethodsOnTypeHierarchy(m => m.Name == name, bindingFlags)) + yield return new SystemReflectionMethodBaseValue(new MethodProxy(method)); + } + + private partial IEnumerable GetNestedTypesOnType(TypeProxy type, string name, BindingFlags? bindingFlags) + { + foreach (var nestedType in type.Type.GetNestedTypesOnType(t => t.Name == name, bindingFlags)) + yield return new SystemTypeValue(new TypeProxy(nestedType)); + } + + private partial bool TryGetBaseType(TypeProxy type, out TypeProxy? baseType) + { + if (type.Type.BaseType != null) + { + baseType = new TypeProxy(type.Type.BaseType); + return true; + } + + baseType = null; + return false; + } + + private partial bool TryResolveTypeNameForCreateInstance(in MethodProxy calledMethod, string assemblyName, string typeName, out TypeProxy resolvedType) + { + // TODO: niche APIs that we probably shouldn't even have added + // We have to issue a warning, otherwise we could break the app without a warning. + // This is not the ideal warning, but it's good enough for now. + _diagnosticContext.AddDiagnostic(DiagnosticId.UnrecognizedParameterInMethodCreateInstance, calledMethod.GetParameterDisplayName(1), calledMethod.GetDisplayName()); + resolvedType = default; + return false; + } + + private partial void MarkStaticConstructor(TypeProxy type) + => _reflectionMarker.MarkStaticConstructor(_diagnosticContext.Origin, type.Type); + + private partial void MarkEventsOnTypeHierarchy(TypeProxy type, string name, BindingFlags? bindingFlags) + => _reflectionMarker.MarkEventsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, e => e.Name == name, _memberWithRequirements, bindingFlags); + + private partial void MarkFieldsOnTypeHierarchy(TypeProxy type, string name, BindingFlags? bindingFlags) + => _reflectionMarker.MarkFieldsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, f => f.Name == name, _memberWithRequirements, bindingFlags); + + private partial void MarkPropertiesOnTypeHierarchy(TypeProxy type, string name, BindingFlags? bindingFlags) + => _reflectionMarker.MarkPropertiesOnTypeHierarchy(_diagnosticContext.Origin, type.Type, p => p.Name == name, _memberWithRequirements, bindingFlags); + + private partial void MarkPublicParameterlessConstructorOnType(TypeProxy type) + => _reflectionMarker.MarkConstructorsOnType(_diagnosticContext.Origin, type.Type, m => m.IsPublic() && m.Signature.Length == 0, _memberWithRequirements); + + private partial void MarkConstructorsOnType(TypeProxy type, BindingFlags? bindingFlags, int? parameterCount) + => _reflectionMarker.MarkConstructorsOnType(_diagnosticContext.Origin, type.Type, parameterCount == null ? null : m => m.Signature.Length == parameterCount, _memberWithRequirements, bindingFlags); + + private partial void MarkMethod(MethodProxy method) + => _reflectionMarker.MarkMethod(_diagnosticContext.Origin, method.Method, _memberWithRequirements); + + private partial void MarkType(TypeProxy type) + => _reflectionMarker.MarkType(_diagnosticContext.Origin, type.Type, _memberWithRequirements); + + private partial bool MarkAssociatedProperty(MethodProxy method) + { + var propertyDefinition = method.Method.GetPropertyForAccessor(); + if (propertyDefinition is null) + { + return false; + } + + _reflectionMarker.MarkProperty(_diagnosticContext.Origin, propertyDefinition, _memberWithRequirements); + return true; + } + + private partial string GetContainingSymbolDisplayName() => _callingMethod.GetDisplayName(); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/IValueWithStaticType.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/IValueWithStaticType.cs new file mode 100644 index 0000000000000..eb08e77d0171e --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/IValueWithStaticType.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Internal.TypeSystem; + +#nullable enable + +namespace ILCompiler.Dataflow +{ + interface IValueWithStaticType + { + /// + /// The IL type of the value, represented as closely as possible, but not always exact. It can be null, for + /// example, when the analysis is imprecise or operating on malformed IL. + /// + TypeDesc? StaticType { get; } + } +} 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 f07166e28d80c..535ae2d1cf2f0 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs @@ -4,10 +4,16 @@ using System; using System.Collections.Generic; using System.Linq; - +using ILLink.Shared.DataFlow; +using ILLink.Shared.TrimAnalysis; +using ILLink.Shared.TypeSystemProxy; using Internal.IL; using Internal.TypeSystem; +using MultiValue = ILLink.Shared.DataFlow.ValueSet; + +#nullable enable + namespace ILCompiler.Dataflow { /// @@ -15,14 +21,26 @@ namespace ILCompiler.Dataflow /// readonly struct StackSlot { - public ValueNode Value { get; } + public MultiValue Value { get; } /// /// True if the value is on the stack as a byref /// public bool IsByRef { get; } - public StackSlot(ValueNode value, bool isByRef = false) + public StackSlot() + { + Value = new MultiValue(UnknownValue.Instance); + IsByRef = false; + } + + public StackSlot(SingleValue value, bool isByRef = false) + { + Value = new MultiValue(value); + IsByRef = isByRef; + } + + public StackSlot(MultiValue value, bool isByRef = false) { Value = value; IsByRef = isByRef; @@ -31,7 +49,9 @@ public StackSlot(ValueNode value, bool isByRef = false) abstract partial class MethodBodyScanner { - internal ValueNode MethodReturnValue { private set; get; } + protected static ValueSetLattice MultiValueLattice => default; + + internal MultiValue ReturnValue { private set; get; } protected virtual void WarnAboutInvalidILInMethod(MethodIL method, int ilOffset) { @@ -78,21 +98,7 @@ private StackSlot PopUnknown(Stack stack, int count, MethodIL method, private static StackSlot MergeStackElement(StackSlot a, StackSlot b) { - StackSlot mergedSlot; - if (b.Value == null) - { - mergedSlot = a; - } - else if (a.Value == null) - { - mergedSlot = b; - } - else - { - mergedSlot = new StackSlot(MergePointValue.MergeValues(a.Value, b.Value)); - } - - return mergedSlot; + return new StackSlot(MultiValueLattice.Meet(a.Value, b.Value)); } // Merge stacks together. This may return the first stack, the stack length must be the same for the two stacks. @@ -121,7 +127,7 @@ private static Stack MergeStack(Stack a, Stack return new Stack(newStack); } - private static void ClearStack(ref Stack stack) + private static void ClearStack(ref Stack? stack) { stack = null; } @@ -188,63 +194,62 @@ public int MoveNext(int offset) } private static void StoreMethodLocalValue( - ValueBasicBlockPair[] valueCollection, - ValueNode valueToStore, + ValueBasicBlockPair?[] valueCollection, + in MultiValue valueToStore, int index, int curBasicBlock) { - ValueBasicBlockPair newValue = new ValueBasicBlockPair { BasicBlockIndex = curBasicBlock }; + MultiValue value; - ValueBasicBlockPair existingValue = valueCollection[index]; - if (existingValue.Value != null - && existingValue.BasicBlockIndex == curBasicBlock) + ValueBasicBlockPair? existingValue = valueCollection[index]; + if (!existingValue.HasValue + || existingValue.Value.BasicBlockIndex == curBasicBlock) { // If the previous value was stored in the current basic block, then we can safely // overwrite the previous value with the new one. - newValue.Value = valueToStore; + value = valueToStore; } else { // If the previous value came from a previous basic block, then some other use of // the local could see the previous value, so we must merge the new value with the // old value. - newValue.Value = MergePointValue.MergeValues(existingValue.Value, valueToStore); + value = MultiValueLattice.Meet(existingValue.Value.Value, valueToStore); } - valueCollection[index] = newValue; + valueCollection[index] = new ValueBasicBlockPair(value, curBasicBlock); } private static void StoreMethodLocalValue( Dictionary valueCollection, - ValueNode valueToStore, + in MultiValue valueToStore, KeyType collectionKey, int curBasicBlock, int? maxTrackedValues = null) + where KeyType : notnull { - ValueBasicBlockPair newValue = new ValueBasicBlockPair { BasicBlockIndex = curBasicBlock }; - - ValueBasicBlockPair existingValue; - if (valueCollection.TryGetValue(collectionKey, out existingValue)) + if (valueCollection.TryGetValue(collectionKey, out ValueBasicBlockPair existingValue)) { + MultiValue value; + if (existingValue.BasicBlockIndex == curBasicBlock) { // If the previous value was stored in the current basic block, then we can safely // overwrite the previous value with the new one. - newValue.Value = valueToStore; + value = valueToStore; } else { // If the previous value came from a previous basic block, then some other use of // the local could see the previous value, so we must merge the new value with the // old value. - newValue.Value = MergePointValue.MergeValues(existingValue.Value, valueToStore); + value = MultiValueLattice.Meet(existingValue.Value, valueToStore); } - valueCollection[collectionKey] = newValue; + valueCollection[collectionKey] = new ValueBasicBlockPair(value, curBasicBlock); } else if (maxTrackedValues == null || valueCollection.Count < maxTrackedValues) { // We're not currently tracking a value a this index, so store the value now. - newValue.Value = valueToStore; - valueCollection[collectionKey] = newValue; + valueCollection[collectionKey] = new ValueBasicBlockPair(valueToStore, curBasicBlock); } } @@ -252,16 +257,16 @@ public void Scan(MethodIL methodBody) { MethodDesc thisMethod = methodBody.OwningMethod; - ValueBasicBlockPair[] locals = new ValueBasicBlockPair[methodBody.GetLocals().Length]; + ValueBasicBlockPair?[] locals = new ValueBasicBlockPair?[methodBody.GetLocals().Length]; Dictionary> knownStacks = new Dictionary>(); - Stack currentStack = new Stack(methodBody.MaxStack); + Stack? currentStack = new Stack(methodBody.MaxStack); ScanExceptionInformation(knownStacks, methodBody); BasicBlockIterator blockIterator = new BasicBlockIterator(methodBody); - MethodReturnValue = null; + ReturnValue = new(); ILReader reader = new ILReader(methodBody.GetILBytes()); while (reader.HasNext) { @@ -509,7 +514,7 @@ public void Scan(MethodIL methodBody) { StackSlot count = PopUnknown(currentStack, 1, methodBody, offset); var arrayElement = (TypeDesc)methodBody.GetObject(reader.ReadILToken()); - currentStack.Push(new StackSlot(new ArrayValue(count.Value, arrayElement))); + currentStack.Push(new StackSlot(ArrayValue.Create(count.Value, arrayElement))); } break; @@ -589,7 +594,8 @@ public void Scan(MethodIL methodBody) case ILOpcode.stloc_1: case ILOpcode.stloc_2: case ILOpcode.stloc_3: - ScanStloc(methodBody, offset, opcode switch { + ScanStloc(methodBody, offset, opcode switch + { ILOpcode.stloc => reader.ReadILUInt16(), ILOpcode.stloc_s => reader.ReadILByte(), _ => opcode - ILOpcode.stloc_0, @@ -674,7 +680,7 @@ public void Scan(MethodIL methodBody) if (hasReturnValue) { StackSlot retValue = PopUnknown(currentStack, 1, methodBody, offset); - MethodReturnValue = MergePointValue.MergeValues(MethodReturnValue, retValue.Value); + ReturnValue = MultiValueLattice.Meet(ReturnValue, retValue.Value); } ClearStack(ref currentStack); break; @@ -742,7 +748,7 @@ private static void ScanExceptionInformation(Dictionary> k } } - protected abstract ValueNode GetMethodParameterValue(MethodDesc method, int parameterIndex); + protected abstract SingleValue GetMethodParameterValue(MethodDesc method, int parameterIndex); private void ScanLdarg(ILOpcode opcode, int paramNum, Stack currentStack, MethodDesc thisMethod) { @@ -771,7 +777,11 @@ Stack currentStack ) { var valueToStore = PopUnknown(currentStack, 1, methodBody, offset); - HandleStoreParameter(methodBody, offset, index, valueToStore.Value); + var targetValue = GetMethodParameterValue(methodBody.OwningMethod, index); + if (targetValue is MethodParameterValue targetParameterValue) + HandleStoreParameter(methodBody, offset, targetParameterValue, valueToStore.Value); + + // If the targetValue is MethodThisValue do nothing - it should never happen really, and if it does, there's nothing we can track there } private void ScanLdloc( @@ -780,20 +790,19 @@ private void ScanLdloc( ILOpcode operation, int index, Stack currentStack, - ValueBasicBlockPair[] locals) + ValueBasicBlockPair?[] locals) { bool isByRef = operation == ILOpcode.ldloca || operation == ILOpcode.ldloca_s || methodBody.GetLocals()[index].Type.IsByRefOrPointer(); - ValueBasicBlockPair localValue = locals[index]; - if (localValue.Value != null) + ValueBasicBlockPair? localValue = locals[index]; + if (!localValue.HasValue) { - ValueNode valueToPush = localValue.Value; - currentStack.Push(new StackSlot(valueToPush, isByRef)); + currentStack.Push(new StackSlot(UnknownValue.Instance, isByRef)); } else { - currentStack.Push(new StackSlot(null, isByRef)); + currentStack.Push(new StackSlot(localValue.Value.Value, isByRef)); } } @@ -808,8 +817,31 @@ private static void ScanLdtoken(MethodIL methodBody, object operand, Stack) will be RuntimeTypeHandleValue / SystemTypeValue + if (type.HasInstantiation && !type.IsGenericDefinition && type.IsTypeOf(ILLink.Shared.TypeSystemProxy.WellKnownType.System_Nullable_T)) + { + switch (type.Instantiation[0]) + { + case GenericParameterDesc genericParam: + var nullableDam = new RuntimeTypeHandleForNullableValueWithDynamicallyAccessedMembers(new TypeProxy(type), + new RuntimeTypeHandleForGenericParameterValue(genericParam)); + currentStack.Push(new StackSlot(nullableDam)); + return; + case MetadataType underlyingType: + var nullableType = new RuntimeTypeHandleForNullableSystemTypeValue(new TypeProxy(type), new SystemTypeValue(underlyingType)); + currentStack.Push(new StackSlot(nullableType)); + return; + default: + PushUnknown(currentStack); + return; + } + } + else + { + var typeHandle = new RuntimeTypeHandleValue(new TypeProxy(type)); + currentStack.Push(new StackSlot(typeHandle)); + return; + } } } else if (operand is MethodDesc method) @@ -828,7 +860,7 @@ private void ScanStloc( int offset, int index, Stack currentStack, - ValueBasicBlockPair[] locals, + ValueBasicBlockPair?[] locals, int curBasicBlock) { StackSlot valueToStore = PopUnknown(currentStack, 1, methodBody, offset); @@ -843,21 +875,21 @@ private void ScanIndirectStore( StackSlot valueToStore = PopUnknown(currentStack, 1, methodBody, offset); StackSlot destination = PopUnknown(currentStack, 1, methodBody, offset); - foreach (var uniqueDestination in destination.Value.UniqueValues()) + foreach (var uniqueDestination in destination.Value) { - if (uniqueDestination.Kind == ValueNodeKind.LoadField) + if (uniqueDestination is FieldValue fieldDestination) { - HandleStoreField(methodBody, offset, ((LoadFieldValue)uniqueDestination).Field, valueToStore.Value); + HandleStoreField(methodBody, offset, fieldDestination, valueToStore.Value); } - else if (uniqueDestination.Kind == ValueNodeKind.MethodParameter) + else if (uniqueDestination is MethodParameterValue parameterDestination) { - HandleStoreParameter(methodBody, offset, ((MethodParameterValue)uniqueDestination).ParameterIndex, valueToStore.Value); + HandleStoreParameter(methodBody, offset, parameterDestination, valueToStore.Value); } } } - protected abstract ValueNode GetFieldValue(MethodIL method, FieldDesc field); + protected abstract MultiValue GetFieldValue(FieldDesc field); private void ScanLdfld( MethodIL methodBody, @@ -872,15 +904,15 @@ Stack currentStack bool isByRef = opcode == ILOpcode.ldflda || opcode == ILOpcode.ldsflda; - StackSlot slot = new StackSlot(GetFieldValue(methodBody, field), isByRef); + StackSlot slot = new StackSlot(GetFieldValue(field), isByRef); currentStack.Push(slot); } - protected virtual void HandleStoreField(MethodIL method, int offset, FieldDesc field, ValueNode valueToStore) + protected virtual void HandleStoreField(MethodIL method, int offset, FieldValue field, MultiValue valueToStore) { } - protected virtual void HandleStoreParameter(MethodIL method, int offset, int index, ValueNode valueToStore) + protected virtual void HandleStoreParameter(MethodIL method, int offset, MethodParameterValue parameter, MultiValue valueToStore) { } @@ -895,7 +927,15 @@ private void ScanStfld( if (opcode == ILOpcode.stfld) PopUnknown(currentStack, 1, methodBody, offset); - HandleStoreField(methodBody, offset, field, valueToStoreSlot.Value); + foreach (var value in GetFieldValue(field)) + { + // GetFieldValue may return different node types, in which case they can't be stored to. + // At least not yet. + if (value is not FieldValue fieldValue) + continue; + + HandleStoreField(methodBody, offset, fieldValue, valueToStoreSlot.Value); + } } private ValueNodeList PopCallArguments( @@ -903,7 +943,7 @@ private ValueNodeList PopCallArguments( MethodDesc methodCalled, MethodIL containingMethodBody, bool isNewObj, int ilOffset, - out ValueNode newObjValue) + out SingleValue? newObjValue) { newObjValue = null; @@ -938,11 +978,11 @@ private void HandleCall( { bool isNewObj = opcode == ILOpcode.newobj; - ValueNode newObjValue; + SingleValue? newObjValue; ValueNodeList methodParams = PopCallArguments(currentStack, calledMethod, callingMethodBody, isNewObj, offset, out newObjValue); - ValueNode methodReturnValue; + MultiValue methodReturnValue; bool handledFunction = HandleCall( callingMethodBody, calledMethod, @@ -957,7 +997,7 @@ private void HandleCall( if (isNewObj) { if (newObjValue == null) - PushUnknown(currentStack); + methodReturnValue = UnknownValue.Instance; else methodReturnValue = newObjValue; } @@ -970,14 +1010,17 @@ private void HandleCall( } } - if (methodReturnValue != null) + if (isNewObj || !calledMethod.Signature.ReturnType.IsVoid) currentStack.Push(new StackSlot(methodReturnValue, calledMethod.Signature.ReturnType.IsByRefOrPointer())); foreach (var param in methodParams) { - if (param is ArrayValue arr) + foreach (var v in param) { - MarkArrayValuesAsUnknown(arr, curBasicBlock); + if (v is ArrayValue arr) + { + MarkArrayValuesAsUnknown(arr, curBasicBlock); + } } } } @@ -988,7 +1031,7 @@ public abstract bool HandleCall( ILOpcode operation, int offset, ValueNodeList methodParams, - out ValueNode methodReturnValue); + out MultiValue methodReturnValue); // Limit tracking array values to 32 values for performance reasons. There are many arrays much longer than 32 elements in .NET, but the interesting ones for the linker are nearly always less than 32 elements. private const int MaxTrackedArrayValues = 32; @@ -1014,7 +1057,7 @@ private void ScanStelem( StackSlot indexToStoreAt = PopUnknown(currentStack, 1, methodBody, offset); StackSlot arrayToStoreIn = PopUnknown(currentStack, 1, methodBody, offset); int? indexToStoreAtInt = indexToStoreAt.Value.AsConstInt(); - foreach (var array in arrayToStoreIn.Value.UniqueValues()) + foreach (var array in arrayToStoreIn.Value) { if (array is ArrayValue arrValue) { @@ -1040,7 +1083,7 @@ private void ScanLdelem( { StackSlot indexToLoadFrom = PopUnknown(currentStack, 1, methodBody, offset); StackSlot arrayToLoadFrom = PopUnknown(currentStack, 1, methodBody, offset); - if (arrayToLoadFrom.Value is not ArrayValue arr) + if (arrayToLoadFrom.Value.AsSingleValue() is not ArrayValue arr) { PushUnknown(currentStack); return; @@ -1058,17 +1101,13 @@ private void ScanLdelem( return; } - - ValueBasicBlockPair arrayIndexValue; - arr.IndexValues.TryGetValue(index.Value, out arrayIndexValue); - if (arrayIndexValue.Value != null) + if (arr.IndexValues.TryGetValue(index.Value, out ValueBasicBlockPair arrayIndexValue)) { - ValueNode valueToPush = arrayIndexValue.Value; - currentStack.Push(new StackSlot(valueToPush, isByRef)); + currentStack.Push(new StackSlot(arrayIndexValue.Value, isByRef)); } else { - currentStack.Push(new StackSlot(null, isByRef)); + PushUnknown(currentStack); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodParameterValue.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodParameterValue.cs new file mode 100644 index 0000000000000..b8e5e72803084 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodParameterValue.cs @@ -0,0 +1,59 @@ +// 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.CodeAnalysis; +using ILCompiler.Dataflow; +using ILLink.Shared.DataFlow; +using Internal.TypeSystem; + +#nullable enable + +namespace ILLink.Shared.TrimAnalysis +{ + + /// + /// A value that came from a method parameter - such as the result of a ldarg. + /// + partial record MethodParameterValue : IValueWithStaticType + { + public MethodParameterValue(MethodDesc method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + StaticType = method.Signature[parameterIndex]; + Method = method; + ParameterIndex = parameterIndex; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public readonly MethodDesc Method; + + /// + /// This is the index of non-implicit parameter - so the index into MethodDesc.Signature array. + /// It's NOT the IL parameter index which could be offset by 1 if the method has an implicit this. + /// + public readonly int ParameterIndex; + + public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } + + public override IEnumerable GetDiagnosticArgumentsForAnnotationMismatch() + => new string[] { DiagnosticUtilities.GetParameterNameForErrorMessage(Method, ParameterIndex), DiagnosticUtilities.GetMethodSignatureDisplayName(Method) }; + + public TypeDesc? StaticType { get; } + + public override SingleValue DeepCopy() => this; // This value is immutable + + public override string ToString() => this.ValueToString(Method, ParameterIndex, DynamicallyAccessedMemberTypes); + + internal ParameterOrigin ParameterOrigin + { + get + { + int index = ParameterIndex; + if (!Method.Signature.IsStatic) + index++; + + return new ParameterOrigin(Method, index); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodProxy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodProxy.cs new file mode 100644 index 0000000000000..ed866e82d32d1 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodProxy.cs @@ -0,0 +1,65 @@ +// 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.Immutable; +using ILCompiler; +using ILCompiler.Dataflow; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +#nullable enable + +namespace ILLink.Shared.TypeSystemProxy +{ + readonly partial struct MethodProxy + { + public MethodProxy(MethodDesc method) => Method = method; + + public static implicit operator MethodProxy(MethodDesc method) => new(method); + + public readonly MethodDesc Method; + + public string Name { get => Method.Name; } + + public string GetDisplayName() => Method.GetDisplayName(); + + internal partial bool IsDeclaredOnType(string fullTypeName) => Method.IsDeclaredOnType(fullTypeName); + + internal partial bool HasParameters() => Method.Signature.Length > 0; + + internal partial int GetParametersCount() => Method.Signature.Length; + + internal partial bool HasParameterOfType(int parameterIndex, string fullTypeName) => Method.HasParameterOfType(parameterIndex, fullTypeName); + + internal partial string GetParameterDisplayName(int parameterIndex) => + (Method is EcmaMethod ecmaMethod) + ? ecmaMethod.GetParameterDisplayName(parameterIndex) + : $"#{parameterIndex}"; + + internal partial bool HasGenericParameters() => Method.HasInstantiation; + + internal partial bool HasGenericParametersCount(int genericParameterCount) => Method.Instantiation.Length == genericParameterCount; + + internal partial ImmutableArray GetGenericParameters() + { + var methodDef = Method.GetMethodDefinition(); + + if (!methodDef.HasInstantiation) + return ImmutableArray.Empty; + + var builder = ImmutableArray.CreateBuilder(methodDef.Instantiation.Length); + foreach (var genericParameter in methodDef.Instantiation) + { + builder.Add(new GenericParameterProxy((GenericParameterDesc)genericParameter)); + } + + return builder.ToImmutableArray(); + } + + internal partial bool IsStatic() => Method.Signature.IsStatic; + + internal partial bool ReturnsVoid() => Method.Signature.ReturnType.IsVoid; + + public override string ToString() => Method.ToString(); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodReturnValue.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodReturnValue.cs new file mode 100644 index 0000000000000..552e971a059e3 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodReturnValue.cs @@ -0,0 +1,39 @@ +// 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.CodeAnalysis; +using ILCompiler.Dataflow; +using ILLink.Shared.DataFlow; +using Internal.TypeSystem; + +#nullable enable + +namespace ILLink.Shared.TrimAnalysis +{ + /// + /// Return value from a method + /// + partial record MethodReturnValue : IValueWithStaticType + { + public MethodReturnValue(MethodDesc method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + StaticType = method.Signature.ReturnType; + Method = method; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public readonly MethodDesc Method; + + public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } + + public override IEnumerable GetDiagnosticArgumentsForAnnotationMismatch() + => new string[] { DiagnosticUtilities.GetMethodSignatureDisplayName(Method) }; + + public TypeDesc? StaticType { get; } + + public override SingleValue DeepCopy() => this; // This value is immutable + + public override string ToString() => this.ValueToString(Method, DynamicallyAccessedMemberTypes); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodThisParameterValue.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodThisParameterValue.cs new file mode 100644 index 0000000000000..b04698e26d07a --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodThisParameterValue.cs @@ -0,0 +1,40 @@ +// 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.CodeAnalysis; +using ILCompiler; +using ILCompiler.Dataflow; +using ILLink.Shared.DataFlow; +using Internal.TypeSystem; + +#nullable enable + +namespace ILLink.Shared.TrimAnalysis +{ + + /// + /// A value that came from the implicit this parameter of a method + /// + partial record MethodThisParameterValue : IValueWithStaticType + { + public MethodThisParameterValue(MethodDesc method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + Method = method; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public readonly MethodDesc Method; + + public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } + + public override IEnumerable GetDiagnosticArgumentsForAnnotationMismatch() + => new string[] { Method.GetDisplayName() }; + + public TypeDesc? StaticType => Method.OwningType; + + public override SingleValue DeepCopy() => this; // This value is immutable + + public override string ToString() => this.ValueToString(Method, DynamicallyAccessedMemberTypes); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ArrayValue.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ArrayValue.cs new file mode 100644 index 0000000000000..71934bc28ca87 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ArrayValue.cs @@ -0,0 +1,121 @@ +// 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.Collections.Generic; +using System.Text; +using ILLink.Shared.DataFlow; +using Mono.Cecil; +using Mono.Linker.Dataflow; +using MultiValue = ILLink.Shared.DataFlow.ValueSet; + + +namespace ILLink.Shared.TrimAnalysis +{ + partial record ArrayValue + { + public static MultiValue Create (MultiValue size, TypeReference elementType) + { + MultiValue result = MultiValueLattice.Top; + foreach (var sizeValue in size) { + result = MultiValueLattice.Meet (result, new MultiValue (new ArrayValue (sizeValue, elementType))); + } + + return result; + } + + public static MultiValue Create (int size, TypeReference elementType) + { + return new MultiValue (new ArrayValue (new ConstIntValue (size), elementType)); + } + + /// + /// Constructs an array value of the given size + /// + ArrayValue (SingleValue size, TypeReference elementType) + { + Size = size; + ElementType = elementType; + IndexValues = new Dictionary (); + } + + public TypeReference ElementType { get; } + public Dictionary IndexValues { get; } + + public partial bool TryGetValueByIndex (int index, out MultiValue value) + { + if (IndexValues.TryGetValue (index, out var valuePair)) { + value = valuePair.Value; + return true; + } + + value = default; + return false; + } + + public override int GetHashCode () + { + return HashCode.Combine (GetType ().GetHashCode (), Size); + } + + public bool Equals (ArrayValue? otherArr) + { + if (otherArr == null) + return false; + + bool equals = Size.Equals (otherArr.Size); + equals &= IndexValues.Count == otherArr.IndexValues.Count; + if (!equals) + return false; + + // If both sets T and O are the same size and "T intersect O" is empty, then T == O. + HashSet> thisValueSet = new (IndexValues); + HashSet> otherValueSet = new (otherArr.IndexValues); + thisValueSet.ExceptWith (otherValueSet); + return thisValueSet.Count == 0; + } + + public override SingleValue DeepCopy () + { + var newValue = new ArrayValue (Size.DeepCopy (), ElementType); + foreach (var kvp in IndexValues) { + newValue.IndexValues.Add (kvp.Key, new ValueBasicBlockPair (kvp.Value.Value.Clone (), kvp.Value.BasicBlockIndex)); + } + + return newValue; + } + + public override string ToString () + { + StringBuilder result = new (); + result.Append ("Array Size:"); + result.Append (this.ValueToString (Size)); + + result.Append (", Values:("); + bool first = true; + foreach (var element in IndexValues) { + if (!first) { + result.Append (","); + first = false; + } + + result.Append ("("); + result.Append (element.Key); + result.Append (",("); + bool firstValue = true; + foreach (var v in element.Value.Value) { + if (firstValue) { + result.Append (","); + firstValue = false; + } + + result.Append (v.ToString ()); + } + result.Append ("))"); + } + result.Append (')'); + + return result.ToString (); + } + } +} \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DiagnosticContext.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DiagnosticContext.cs new file mode 100644 index 0000000000000..df43246bbb815 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DiagnosticContext.cs @@ -0,0 +1,23 @@ +// 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 Mono.Linker; + +namespace ILLink.Shared.TrimAnalysis +{ + readonly partial struct DiagnosticContext + { + public readonly MessageOrigin Origin; + public readonly bool DiagnosticsEnabled; + readonly LinkContext _context; + + public DiagnosticContext (in MessageOrigin origin, bool diagnosticsEnabled, LinkContext context) + => (Origin, DiagnosticsEnabled, _context) = (origin, diagnosticsEnabled, context); + + public partial void AddDiagnostic (DiagnosticId id, params string[] args) + { + if (DiagnosticsEnabled) + _context.LogWarning (Origin, id, args); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DiagnosticUtilities.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DiagnosticUtilities.cs index 9aef3fde6d237..e354b683e048d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DiagnosticUtilities.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DiagnosticUtilities.cs @@ -1,5 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// 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 Mono.Cecil; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DynamicallyAccessedMembersBinder.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DynamicallyAccessedMembersBinder.cs index b44dd1cd1cd86..26b67b5531095 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DynamicallyAccessedMembersBinder.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DynamicallyAccessedMembersBinder.cs @@ -1,21 +1,15 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// 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.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using ILLink.Shared; using Mono.Cecil; namespace Mono.Linker { - // Temporary workaround - should be removed once linker can be upgraded to build against - // high enough version of the framework which has this enum value. - internal static class DynamicallyAccessedMemberTypesOverlay - { - public const DynamicallyAccessedMemberTypes Interfaces = (DynamicallyAccessedMemberTypes) 0x2000; - } - internal static class DynamicallyAccessedMembersBinder { // Returns the members of the type bound by memberTypes. For DynamicallyAccessedMemberTypes.All, this returns all members of the type and its @@ -427,4 +421,4 @@ static void GetAllOnType (TypeDefinition type, LinkContext context, bool declare } } } -} \ No newline at end of file +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DynamicallyAccessedMembersTypeHierarchy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DynamicallyAccessedMembersTypeHierarchy.cs new file mode 100644 index 0000000000000..56e9496e84e8f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/DynamicallyAccessedMembersTypeHierarchy.cs @@ -0,0 +1,265 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using ILLink.Shared; +using Mono.Cecil; +using Mono.Linker.Steps; + +namespace Mono.Linker.Dataflow +{ + class DynamicallyAccessedMembersTypeHierarchy + { + readonly LinkContext _context; + readonly MarkStep _markStep; + + // Cache of DynamicallyAccessedMembers annotations applied to types and their hierarchies + // Values + // annotation - the aggregated annotation value from the entire base and interface hierarchy of the given type + // If the type has a base class with annotation a1 and an interface with annotation a2, the stored + // annotation is a1 | a2. + // applied - set to true once the annotation was applied to the type + // This only happens once the right reflection pattern is found. + // If a new type is being marked and one of its base types/interface has the applied set to true + // the new type will apply its annotation and will also set its applied to true. + // Non-interface types + // - Only marked types with non-empty annotation are put into the cache + // - Non-marked types are not stored in the cache + // - Marked types which are not in the cache don't have any annotation + // Interface types + // - All interface types accessible from marked types are stored in the cache + // - If the interface type doesn't have annotation the value None is stored here + // + // It's not possible to use the marking as a filter for interfaces in the cache + // because interfaces are marked late and in effectively random order. + // For this cache to be effective we need to be able to fill it for all base types and interfaces + // of a type which is currently being marked - at which point the interfaces are not yet marked. + readonly Dictionary _typesInDynamicallyAccessedMembersHierarchy; + + public DynamicallyAccessedMembersTypeHierarchy (LinkContext context, MarkStep markStep) + { + _context = context; + _markStep = markStep; + _typesInDynamicallyAccessedMembersHierarchy = new Dictionary (); + } + + public (DynamicallyAccessedMemberTypes annotation, bool applied) ProcessMarkedTypeForDynamicallyAccessedMembersHierarchy (TypeDefinition type) + { + // We'll use the cache also as a way to detect and avoid recursion for interfaces and annotated base types + if (_typesInDynamicallyAccessedMembersHierarchy.TryGetValue (type, out var existingValue)) + return existingValue; + + DynamicallyAccessedMemberTypes annotation = _context.Annotations.FlowAnnotations.GetTypeAnnotation (type); + bool apply = false; + + if (type.IsInterface) + _typesInDynamicallyAccessedMembersHierarchy.Add (type, (annotation, false)); + + TypeDefinition? baseType = _context.TryResolve (type.BaseType); + if (baseType != null) { + var baseValue = ProcessMarkedTypeForDynamicallyAccessedMembersHierarchy (baseType); + annotation |= baseValue.annotation; + apply |= baseValue.applied; + } + + // For the purposes of the DynamicallyAccessedMembers type hierarchies + // we consider interfaces of marked types to be also "marked" in that + // their annotations will be applied to the type regardless if later on + // we decide to remove the interface. This is to keep the complexity of the implementation + // relatively low. In the future it could be possibly optimized. + if (type.HasInterfaces) { + foreach (InterfaceImplementation iface in type.Interfaces) { + var interfaceType = _context.TryResolve (iface.InterfaceType); + if (interfaceType != null) { + var interfaceValue = ProcessMarkedTypeForDynamicallyAccessedMembersHierarchy (interfaceType); + annotation |= interfaceValue.annotation; + apply |= interfaceValue.applied; + } + } + } + + Debug.Assert (!apply || annotation != DynamicallyAccessedMemberTypes.None); + + // If OptimizeTypeHierarchyAnnotations is disabled, we will apply the annotations without seeing object.GetType() + bool applyOptimizeTypeHierarchyAnnotations = (annotation != DynamicallyAccessedMemberTypes.None) && !_context.IsOptimizationEnabled (CodeOptimizations.OptimizeTypeHierarchyAnnotations, type); + // Unfortunately, we cannot apply the annotation to type derived from EventSource - Revisit after https://github.com/dotnet/runtime/issues/54859 + // Breaking the logic to make it easier to maintain in the future since the logic is convoluted + // DisableEventSourceSpecialHandling is closely tied to a type derived from EventSource and should always go together + // However, logically it should be possible to use DisableEventSourceSpecialHandling to allow marking types derived from EventSource when OptimizeTypeHierarchyAnnotations is disabled + apply |= applyOptimizeTypeHierarchyAnnotations && (_context.DisableEventSourceSpecialHandling || !BCL.EventTracingForWindows.IsEventSourceImplementation (type, _context)); + + // Store the results in the cache + // Don't store empty annotations for non-interface types - we can use the presence of the row + // in the cache as indication of it instead. + // This doesn't work for interfaces, since we can't rely on them being marked (and thus have the cache + // already filled), so we need to always store the row (even if empty) for interfaces. + if (annotation != DynamicallyAccessedMemberTypes.None || type.IsInterface) { + _typesInDynamicallyAccessedMembersHierarchy[type] = (annotation, apply); + } + + // It's important to first store the annotation in the cache and only then apply the annotation. + // Applying the annotation will lead to marking additional types which in turn calls back into this + // method to look for annotations. If the newly marked type derives from the one we're processing + // it will rely on the cache to know if it's annotated - so the record must be in the cache + // before it happens. + if (apply) { + // One of the base/interface types is already marked as having the annotation applied + // so we need to apply the annotation to this type as well + var origin = new MessageOrigin (type); + var reflectionMarker = new ReflectionMarker (_context, _markStep); + // Report warnings on access to annotated members, with the annotated type as the origin. + ApplyDynamicallyAccessedMembersToType (reflectionMarker, origin, type, annotation); + } + + return (annotation, apply); + } + + public DynamicallyAccessedMemberTypes ApplyDynamicallyAccessedMembersToTypeHierarchy ( + ReflectionMarker reflectionMarker, + TypeDefinition type) + { + (var annotation, var applied) = ProcessMarkedTypeForDynamicallyAccessedMembersHierarchy (type); + + // If the annotation was already applied to this type, there's no reason to repeat the operation, the result will + // be no change. + if (applied || annotation == DynamicallyAccessedMemberTypes.None) + return annotation; + + // Apply the effective annotation for the type + var origin = new MessageOrigin (type); + // Report warnings on access to annotated members, with the annotated type as the origin. + ApplyDynamicallyAccessedMembersToType (reflectionMarker, origin, type, annotation); + + // Mark it as applied in the cache + _typesInDynamicallyAccessedMembersHierarchy[type] = (annotation, true); + + // Propagate the newly applied annotation to all derived/implementation types + // Since we don't have a data structure which would allow us to enumerate all derived/implementation types + // walk all of the types in the cache. These are good candidates as types not in the cache don't apply. + // + // Applying annotations can lead to marking additional types which can lead to adding new records + // to the cache. So we can't simply iterate over the cache. We also can't rely on the auto-applying annotations + // which is triggered from marking via ProcessMarkedTypeForDynamicallyAccessedMembersHierarchy as that will + // only reliably work once the annotations are applied to all types in the cache first. Partially + // applied annotations to the cache are not enough. So we have to apply the annotations to any types + // added to the cache during the application as well. + // + HashSet typesProcessed = new HashSet (); + List candidateTypes = new List (); + while (true) { + candidateTypes.Clear (); + foreach (var candidate in _typesInDynamicallyAccessedMembersHierarchy) { + if (candidate.Value.annotation == DynamicallyAccessedMemberTypes.None || candidate.Value.applied) + continue; + + if (typesProcessed.Add (candidate.Key)) + candidateTypes.Add (candidate.Key); + } + + if (candidateTypes.Count == 0) + break; + + foreach (var candidateType in candidateTypes) { + ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMarker, candidateType); + } + } + + return annotation; + } + + bool ApplyDynamicallyAccessedMembersToTypeHierarchyInner ( + in ReflectionMarker reflectionMarker, + TypeDefinition type) + { + (var annotation, var applied) = GetCachedInfoForTypeInHierarchy (type); + + if (annotation == DynamicallyAccessedMemberTypes.None) + return false; + + if (applied) + return true; + + TypeDefinition? baseType = _context.TryResolve (type.BaseType); + if (baseType != null) + applied = ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMarker, baseType); + + if (!applied && type.HasInterfaces) { + foreach (InterfaceImplementation iface in type.Interfaces) { + var interfaceType = _context.TryResolve (iface.InterfaceType); + if (interfaceType != null) { + if (ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMarker, interfaceType)) { + applied = true; + break; + } + } + } + } + + if (applied) { + var origin = new MessageOrigin (type); + // Report warnings on access to annotated members, with the annotated type as the origin. + ApplyDynamicallyAccessedMembersToType (reflectionMarker, origin, type, annotation); + _typesInDynamicallyAccessedMembersHierarchy[type] = (annotation, true); + } + + return applied; + } + + void ApplyDynamicallyAccessedMembersToType (in ReflectionMarker reflectionMarker, in MessageOrigin origin, TypeDefinition type, DynamicallyAccessedMemberTypes annotation) + { + Debug.Assert (annotation != DynamicallyAccessedMemberTypes.None); + + // We need to apply annotations to this type, and its base/interface types (recursively) + // But the annotations on base/interfaces are already applied so we don't need to apply those + // again (and should avoid doing so as it would produce extra warnings). + var baseType = _context.TryResolve (type.BaseType); + if (baseType != null) { + var baseAnnotation = GetCachedInfoForTypeInHierarchy (baseType); + var annotationToApplyToBase = Annotations.GetMissingMemberTypes (annotation, baseAnnotation.annotation); + + // Apply any annotations that didn't exist on the base type to the base type. + // This may produce redundant warnings when the annotation is DAMT.All or DAMT.PublicConstructors and the base already has a + // subset of those annotations. + reflectionMarker.MarkTypeForDynamicallyAccessedMembers (origin, baseType, annotationToApplyToBase, DependencyKind.DynamicallyAccessedMemberOnType, declaredOnly: false); + } + + // Most of the DynamicallyAccessedMemberTypes don't select members on interfaces. We only need to apply + // annotations to interfaces separately if dealing with DAMT.All or DAMT.Interfaces. + if (annotation.HasFlag (DynamicallyAccessedMemberTypesOverlay.Interfaces) && type.HasInterfaces) { + var annotationToApplyToInterfaces = annotation == DynamicallyAccessedMemberTypes.All ? annotation : DynamicallyAccessedMemberTypesOverlay.Interfaces; + foreach (var iface in type.Interfaces) { + var interfaceType = _context.TryResolve (iface.InterfaceType); + if (interfaceType == null) + continue; + + var interfaceAnnotation = GetCachedInfoForTypeInHierarchy (interfaceType); + if (interfaceAnnotation.annotation.HasFlag (annotationToApplyToInterfaces)) + continue; + + // Apply All or Interfaces to the interface type. + // DAMT.All may produce redundant warnings from implementing types, when the interface type already had some annotations. + reflectionMarker.MarkTypeForDynamicallyAccessedMembers (origin, interfaceType, annotationToApplyToInterfaces, DependencyKind.DynamicallyAccessedMemberOnType, declaredOnly: false); + } + } + + // The annotations this type inherited from its base types or interfaces should not produce + // warnings on the respective base/interface members, since those are already covered by applying + // the annotations to those types. So we only need to handle the members directly declared on this type. + reflectionMarker.MarkTypeForDynamicallyAccessedMembers (origin, type, annotation, DependencyKind.DynamicallyAccessedMemberOnType, declaredOnly: true); + } + + (DynamicallyAccessedMemberTypes annotation, bool applied) GetCachedInfoForTypeInHierarchy (TypeDefinition type) + { + // The type should be in our cache already + if (!_typesInDynamicallyAccessedMembersHierarchy.TryGetValue (type, out var existingValue)) { + // If it's not in the cache it should be a non-interface type in which case it means there were no annotations + Debug.Assert (!type.IsInterface); + return (DynamicallyAccessedMemberTypes.None, false); + } + + return existingValue; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/FieldValue.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/FieldValue.cs new file mode 100644 index 0000000000000..6d20f8301c792 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/FieldValue.cs @@ -0,0 +1,41 @@ +// 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.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using ILLink.Shared.DataFlow; +using Mono.Linker; +using Mono.Linker.Dataflow; +using FieldDefinition = Mono.Cecil.FieldDefinition; +using TypeDefinition = Mono.Cecil.TypeDefinition; + + +namespace ILLink.Shared.TrimAnalysis +{ + + /// + /// A representation of a field. Typically a result of ldfld. + /// + sealed partial record FieldValue : IValueWithStaticType + { + public FieldValue (TypeDefinition? staticType, FieldDefinition fieldToLoad, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + StaticType = staticType; + Field = fieldToLoad; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public readonly FieldDefinition Field; + + public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } + + public override IEnumerable GetDiagnosticArgumentsForAnnotationMismatch () + => new string[] { Field.GetDisplayName () }; + + public TypeDefinition? StaticType { get; } + + public override SingleValue DeepCopy () => this; // This value is immutable + + public override string ToString () => this.ValueToString (Field, DynamicallyAccessedMemberTypes); + } +} \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/FlowAnnotations.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/FlowAnnotations.cs index 4f4a50dae205a..aea413b2c7ce4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/FlowAnnotations.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/FlowAnnotations.cs @@ -1,16 +1,19 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// 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.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using ILLink.Shared.TypeSystemProxy; using Mono.Cecil; using Mono.Cecil.Cil; +using Mono.Linker; +using Mono.Linker.Dataflow; -namespace Mono.Linker.Dataflow +namespace ILLink.Shared.TrimAnalysis { - class FlowAnnotations + partial class FlowAnnotations { readonly LinkContext _context; readonly Dictionary _annotations = new Dictionary (); @@ -169,9 +172,7 @@ DynamicallyAccessedMemberTypes GetMemberTypesForDynamicallyAccessedMembersAttrib if (attribute.ConstructorArguments.Count == 1) return (DynamicallyAccessedMemberTypes) (int) attribute.ConstructorArguments[0].Value; else - _context.LogWarning ( - $"Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' doesn't have the required number of parameters specified.", - 2028, member); + _context.LogWarning (member, DiagnosticId.AttributeDoesntHaveTheRequiredNumberOfParameters, "System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute"); } return DynamicallyAccessedMemberTypes.None; } @@ -193,9 +194,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) if (!IsTypeInterestingForDataflow (field.FieldType)) { // Already know that there's a non-empty annotation on a field which is not System.Type/String and we're about to ignore it - _context.LogWarning ( - $"Field '{field.GetDisplayName ()}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to fields of type 'System.Type' or 'System.String'.", - 2097, field, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (field, DiagnosticId.DynamicallyAccessedMembersOnFieldCanOnlyApplyToTypesOrStrings, field.GetDisplayName ()); continue; } @@ -227,16 +226,12 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) paramAnnotations[0] = methodMemberTypes; } } else if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) { - _context.LogWarning ( - $"The 'DynamicallyAccessedMembersAttribute' is not allowed on methods. It is allowed on method return value or method parameters though.", - 2041, method, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (method, DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnMethods); } } else { offset = 0; if (methodMemberTypes != DynamicallyAccessedMemberTypes.None) { - _context.LogWarning ( - $"The 'DynamicallyAccessedMembersAttribute' is not allowed on methods. It is allowed on method return value or method parameters though.", - 2041, method, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (method, DiagnosticId.DynamicallyAccessedMembersIsNotAllowedOnMethods); } } @@ -247,9 +242,8 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) continue; if (!IsTypeInterestingForDataflow (methodParameter.ParameterType)) { - _context.LogWarning ( - $"Parameter '{DiagnosticUtilities.GetParameterNameForErrorMessage (methodParameter)}' of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (methodParameter.Method)}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to parameters of type 'System.Type' or 'System.String'.", - 2098, method, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (method, DiagnosticId.DynamicallyAccessedMembersOnMethodParameterCanOnlyApplyToTypesOrStrings, + DiagnosticUtilities.GetParameterNameForErrorMessage (methodParameter), DiagnosticUtilities.GetMethodSignatureDisplayName (methodParameter.Method)); continue; } @@ -261,9 +255,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) DynamicallyAccessedMemberTypes returnAnnotation = GetMemberTypesForDynamicallyAccessedMembersAttribute (method, providerIfNotMember: method.MethodReturnType); if (returnAnnotation != DynamicallyAccessedMemberTypes.None && !IsTypeInterestingForDataflow (method.ReturnType)) { - _context.LogWarning ( - $"Return type of method '{method.GetDisplayName ()}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to properties of type 'System.Type' or 'System.String'.", - 2106, method, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (method, DiagnosticId.DynamicallyAccessedMembersOnMethodReturnValueCanOnlyApplyToTypesOrStrings, method.GetDisplayName ()); } DynamicallyAccessedMemberTypes[]? genericParameterAnnotations = null; @@ -306,9 +298,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) continue; if (!IsTypeInterestingForDataflow (property.PropertyType)) { - _context.LogWarning ( - $"Property '{property.GetDisplayName ()}' has 'DynamicallyAccessedMembersAttribute', but that attribute can only be applied to properties of type 'System.Type' or 'System.String'.", - 2099, property, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (property, DiagnosticId.DynamicallyAccessedMembersOnPropertyCanOnlyApplyToTypesOrStrings, property.GetDisplayName ()); continue; } @@ -328,9 +318,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) } if (annotatedMethods.Any (a => a.Method == setMethod)) { - _context.LogWarning ( - $"'DynamicallyAccessedMembersAttribute' on property '{property.GetDisplayName ()}' conflicts with the same attribute on its accessor '{setMethod.GetDisplayName ()}'.", - 2043, setMethod, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (setMethod, DiagnosticId.DynamicallyAccessedMembersConflictsBetweenPropertyAndAccessor, property.GetDisplayName (), setMethod.GetDisplayName ()); } else { int offset = setMethod.HasImplicitThis () ? 1 : 0; if (setMethod.Parameters.Count > 0) { @@ -357,9 +345,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) } if (annotatedMethods.Any (a => a.Method == getMethod)) { - _context.LogWarning ( - $"'DynamicallyAccessedMembersAttribute' on property '{property.GetDisplayName ()}' conflicts with the same attribute on its accessor '{getMethod.GetDisplayName ()}'.", - 2043, getMethod, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (getMethod, DiagnosticId.DynamicallyAccessedMembersConflictsBetweenPropertyAndAccessor, property.GetDisplayName (), getMethod.GetDisplayName ()); } else { annotatedMethods.Add (new MethodAnnotations (getMethod, null, annotation, null)); } @@ -368,9 +354,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) FieldDefinition? backingField; if (backingFieldFromGetter != null && backingFieldFromSetter != null && backingFieldFromGetter != backingFieldFromSetter) { - _context.LogWarning ( - $"Could not find a unique backing field for property '{property.GetDisplayName ()}' to propagate 'DynamicallyAccessedMembersAttribute'.", - 2042, property, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (property, DiagnosticId.DynamicallyAccessedMembersCouldNotFindBackingField, property.GetDisplayName ()); backingField = null; } else { backingField = backingFieldFromGetter ?? backingFieldFromSetter; @@ -378,9 +362,7 @@ TypeAnnotations BuildTypeAnnotations (TypeDefinition type) if (backingField != null) { if (annotatedFields.Any (a => a.Field == backingField)) { - _context.LogWarning ( - $"'DynamicallyAccessedMemberAttribute' on property '{property.GetDisplayName ()}' conflicts with the same attribute on its backing field '{backingField.GetDisplayName ()}'.", - 2056, backingField, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (backingField, DiagnosticId.DynamicallyAccessedMembersOnPropertyConflictsWithBackingField, property.GetDisplayName (), backingField.GetDisplayName ()); } else { annotatedFields.Add (new FieldAnnotation (backingField, annotation)); } @@ -548,34 +530,24 @@ void LogValidationWarning (IMetadataTokenProvider provider, IMetadataTokenProvid switch (provider) { case ParameterDefinition parameterDefinition: var baseParameterDefinition = (ParameterDefinition) baseProvider; - _context.LogWarning ( - $"'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the parameter '{DiagnosticUtilities.GetParameterNameForErrorMessage (parameterDefinition)}' of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (parameterDefinition.Method)}' " + - $"don't match overridden parameter '{DiagnosticUtilities.GetParameterNameForErrorMessage (baseParameterDefinition)}' of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (baseParameterDefinition.Method)}'. " + - $"All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.", - 2092, origin, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (origin, DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodParameterBetweenOverrides, + DiagnosticUtilities.GetParameterNameForErrorMessage (parameterDefinition), DiagnosticUtilities.GetMethodSignatureDisplayName (parameterDefinition.Method), + DiagnosticUtilities.GetParameterNameForErrorMessage (baseParameterDefinition), DiagnosticUtilities.GetMethodSignatureDisplayName (baseParameterDefinition.Method)); break; case MethodReturnType methodReturnType: - _context.LogWarning ( - $"'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the return value of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (methodReturnType.Method)}' " + - $"don't match overridden return value of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (((MethodReturnType) baseProvider).Method)}'. " + - $"All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.", - 2093, origin, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (origin, DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodReturnValueBetweenOverrides, + DiagnosticUtilities.GetMethodSignatureDisplayName (methodReturnType.Method), DiagnosticUtilities.GetMethodSignatureDisplayName (((MethodReturnType) baseProvider).Method)); break; // No fields - it's not possible to have a virtual field and override it case MethodDefinition methodDefinition: - _context.LogWarning ( - $"'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the implicit 'this' parameter of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (methodDefinition)}' " + - $"don't match overridden implicit 'this' parameter of method '{DiagnosticUtilities.GetMethodSignatureDisplayName ((MethodDefinition) baseProvider)}'. " + - $"All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.", - 2094, origin, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (origin, DiagnosticId.DynamicallyAccessedMembersMismatchOnImplicitThisBetweenOverrides, + DiagnosticUtilities.GetMethodSignatureDisplayName (methodDefinition), DiagnosticUtilities.GetMethodSignatureDisplayName ((MethodDefinition) baseProvider)); break; case GenericParameter genericParameterOverride: var genericParameterBase = (GenericParameter) baseProvider; - _context.LogWarning ( - $"'DynamicallyAccessedMemberTypes' in 'DynamicallyAccessedMembersAttribute' on the generic parameter '{genericParameterOverride.Name}' of '{DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (genericParameterOverride)}' " + - $"don't match overridden generic parameter '{genericParameterBase.Name}' of '{DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (genericParameterBase)}'. " + - $"All overridden members must have the same 'DynamicallyAccessedMembersAttribute' usage.", - 2095, origin, subcategory: MessageSubCategory.TrimAnalysis); + _context.LogWarning (origin, DiagnosticId.DynamicallyAccessedMembersMismatchOnGenericParameterBetweenOverrides, + genericParameterOverride.Name, DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (genericParameterOverride), + genericParameterBase.Name, DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (genericParameterBase)); break; default: throw new NotImplementedException ($"Unsupported provider type '{provider.GetType ()}'."); @@ -696,5 +668,31 @@ readonly struct FieldAnnotation public FieldAnnotation (FieldDefinition field, DynamicallyAccessedMemberTypes annotation) => (Field, Annotation) = (field, annotation); } + + internal partial bool MethodRequiresDataFlowAnalysis (MethodProxy method) + => RequiresDataFlowAnalysis (method.Method); + + internal partial MethodReturnValue GetMethodReturnValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => new MethodReturnValue (method.Method.ReturnType.ResolveToTypeDefinition (_context), method.Method, dynamicallyAccessedMemberTypes); + + internal partial MethodReturnValue GetMethodReturnValue (MethodProxy method) + => GetMethodReturnValue (method, GetReturnParameterAnnotation (method.Method)); + + internal partial GenericParameterValue GetGenericParameterValue (GenericParameterProxy genericParameter) + => new GenericParameterValue (genericParameter.GenericParameter, GetGenericParameterAnnotation (genericParameter.GenericParameter)); + +#pragma warning disable CA1822 // Mark members as static - keep this an instance method for consistency with the others + internal partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => new MethodThisParameterValue (method.Method, dynamicallyAccessedMemberTypes); +#pragma warning restore CA1822 + + internal partial MethodThisParameterValue GetMethodThisParameterValue (MethodProxy method) + => GetMethodThisParameterValue (method, GetParameterAnnotation (method.Method, 0)); + + internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + => new (method.Method.Parameters[parameterIndex].ParameterType.ResolveToTypeDefinition (_context), method.Method, parameterIndex, dynamicallyAccessedMemberTypes); + + internal partial MethodParameterValue GetMethodParameterValue (MethodProxy method, int parameterIndex) + => GetMethodParameterValue (method, parameterIndex, GetParameterAnnotation (method.Method, parameterIndex + (method.IsStatic () ? 0 : 1))); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/GenericParameterProxy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/GenericParameterProxy.cs new file mode 100644 index 0000000000000..1c92df4559c5c --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/GenericParameterProxy.cs @@ -0,0 +1,20 @@ +// 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 Mono.Cecil; + +namespace ILLink.Shared.TypeSystemProxy +{ + internal readonly partial struct GenericParameterProxy + { + public GenericParameterProxy (GenericParameter genericParameter) => GenericParameter = genericParameter; + + public static implicit operator GenericParameterProxy (GenericParameter genericParameter) => new (genericParameter); + + internal partial bool HasDefaultConstructorConstraint () => GenericParameter.HasDefaultConstructorConstraint; + + public readonly GenericParameter GenericParameter; + + public override string ToString () => GenericParameter.ToString (); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/GenericParameterValue.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/GenericParameterValue.cs new file mode 100644 index 0000000000000..8f1d6069ea3fb --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/GenericParameterValue.cs @@ -0,0 +1,33 @@ +// 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.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using ILLink.Shared.DataFlow; +using Mono.Linker.Dataflow; +using GenericParameter = Mono.Cecil.GenericParameter; + +namespace ILLink.Shared.TrimAnalysis +{ + /// + /// This is a System.Type value which represents generic parameter (basically result of typeof(T)) + /// Its actual type is unknown, but it can have annotations. + /// + partial record GenericParameterValue + { + public GenericParameterValue (GenericParameter genericParameter, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + GenericParameter = new (genericParameter); + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } + + public override IEnumerable GetDiagnosticArgumentsForAnnotationMismatch () + => new string[] { GenericParameter.GenericParameter.Name, DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (GenericParameter.GenericParameter) }; + + public override SingleValue DeepCopy () => this; // This value is immutable + + public override string ToString () => this.ValueToString (GenericParameter, DynamicallyAccessedMemberTypes); + } +} \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/HandleCallAction.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/HandleCallAction.cs new file mode 100644 index 0000000000000..3d402cef977ec --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/HandleCallAction.cs @@ -0,0 +1,133 @@ +// 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.Collections.Generic; +using System.Reflection; +using ILLink.Shared.TypeSystemProxy; +using Mono.Cecil; +using Mono.Linker; +using Mono.Linker.Dataflow; + +namespace ILLink.Shared.TrimAnalysis +{ + partial struct HandleCallAction + { +#pragma warning disable CA1822 // Mark members as static - the other partial implementations might need to be instance methods + + readonly LinkContext _context; + readonly ReflectionMarker _reflectionMarker; + readonly MethodDefinition _callingMethodDefinition; + + public HandleCallAction ( + LinkContext context, + ReflectionMarker reflectionMarker, + in DiagnosticContext diagnosticContext, + MethodDefinition callingMethodDefinition) + { + _context = context; + _reflectionMarker = reflectionMarker; + _diagnosticContext = diagnosticContext; + _callingMethodDefinition = callingMethodDefinition; + _annotations = context.Annotations.FlowAnnotations; + _requireDynamicallyAccessedMembersAction = new (context, reflectionMarker, diagnosticContext); + } + + private partial bool MethodIsTypeConstructor (MethodProxy method) + { + if (!method.Method.IsConstructor) + return false; + TypeDefinition? type = method.Method.DeclaringType; + while (type is not null) { + if (type.IsTypeOf (WellKnownType.System_Type)) + return true; + type = _context.Resolve (type.BaseType); + } + return false; + } + + private partial IEnumerable GetMethodsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags) + { + foreach (var method in type.Type.GetMethodsOnTypeHierarchy (_context, m => m.Name == name, bindingFlags)) + yield return new SystemReflectionMethodBaseValue (new MethodProxy (method)); + } + + private partial IEnumerable GetNestedTypesOnType (TypeProxy type, string name, BindingFlags? bindingFlags) + { + foreach (var nestedType in type.Type.GetNestedTypesOnType (t => t.Name == name, bindingFlags)) + yield return new SystemTypeValue (new TypeProxy (nestedType)); + } + + private partial bool TryGetBaseType (TypeProxy type, out TypeProxy? baseType) + { + if (type.Type.BaseType is TypeReference baseTypeRef && _context.TryResolve (baseTypeRef) is TypeDefinition baseTypeDefinition) { + baseType = new TypeProxy (baseTypeDefinition); + return true; + } + + baseType = null; + return false; + } + + private partial bool TryResolveTypeNameForCreateInstance (in MethodProxy calledMethod, string assemblyName, string typeName, out TypeProxy resolvedType) + { + var resolvedAssembly = _context.TryResolve (assemblyName); + if (resolvedAssembly == null) { + _diagnosticContext.AddDiagnostic (DiagnosticId.UnresolvedAssemblyInCreateInstance, + assemblyName, + calledMethod.GetDisplayName ()); + resolvedType = default; + return false; + } + + if (!_context.TypeNameResolver.TryResolveTypeName (resolvedAssembly, typeName, out TypeReference? typeRef) + || _context.TryResolve (typeRef) is not TypeDefinition resolvedTypeDefinition + || typeRef is ArrayType) { + // It's not wrong to have a reference to non-existing type - the code may well expect to get an exception in this case + // Note that we did find the assembly, so it's not a linker config problem, it's either intentional, or wrong versions of assemblies + // but linker can't know that. In case a user tries to create an array using System.Activator we should simply ignore it, the user + // might expect an exception to be thrown. + resolvedType = default; + return false; + } + + resolvedType = new TypeProxy (resolvedTypeDefinition); + return true; + } + + private partial void MarkStaticConstructor (TypeProxy type) + => _reflectionMarker.MarkStaticConstructor (_diagnosticContext.Origin, type.Type); + + private partial void MarkEventsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags) + => _reflectionMarker.MarkEventsOnTypeHierarchy (_diagnosticContext.Origin, type.Type, e => e.Name == name, bindingFlags); + + private partial void MarkFieldsOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags) + => _reflectionMarker.MarkFieldsOnTypeHierarchy (_diagnosticContext.Origin, type.Type, f => f.Name == name, bindingFlags); + + private partial void MarkPropertiesOnTypeHierarchy (TypeProxy type, string name, BindingFlags? bindingFlags) + => _reflectionMarker.MarkPropertiesOnTypeHierarchy (_diagnosticContext.Origin, type.Type, p => p.Name == name, bindingFlags); + + private partial void MarkPublicParameterlessConstructorOnType (TypeProxy type) + => _reflectionMarker.MarkConstructorsOnType (_diagnosticContext.Origin, type.Type, m => m.IsPublic && m.Parameters.Count == 0); + + private partial void MarkConstructorsOnType (TypeProxy type, BindingFlags? bindingFlags, int? parameterCount) + => _reflectionMarker.MarkConstructorsOnType (_diagnosticContext.Origin, type.Type, parameterCount == null ? null : m => m.Parameters.Count == parameterCount, bindingFlags); + + private partial void MarkMethod (MethodProxy method) + => _reflectionMarker.MarkMethod (_diagnosticContext.Origin, method.Method); + + private partial void MarkType (TypeProxy type) + => _reflectionMarker.MarkType (_diagnosticContext.Origin, type.Type); + + private partial bool MarkAssociatedProperty (MethodProxy method) + { + if (method.Method.TryGetProperty (out PropertyDefinition? propertyDefinition)) { + _reflectionMarker.MarkProperty (_diagnosticContext.Origin, propertyDefinition); + return true; + } + + return false; + } + + private partial string GetContainingSymbolDisplayName () => _callingMethodDefinition.GetDisplayName (); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/IValueWithStaticType.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/IValueWithStaticType.cs new file mode 100644 index 0000000000000..e18561f1e5d83 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/IValueWithStaticType.cs @@ -0,0 +1,16 @@ +// 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 Mono.Cecil; + +namespace Mono.Linker.Dataflow +{ + interface IValueWithStaticType + { + /// + /// The IL type of the value, represented as closely as possible, but not always exact. It can be null, for + /// example, when the analysis is imprecise or operating on malformed IL. + /// + TypeDefinition? StaticType { get; } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodBodyScanner.cs index f3cce6d4b8cd1..b50de89788f1f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodBodyScanner.cs @@ -1,12 +1,16 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// 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.Collections.Generic; using System.Linq; +using ILLink.Shared.DataFlow; +using ILLink.Shared.TrimAnalysis; +using ILLink.Shared.TypeSystemProxy; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; +using MultiValue = ILLink.Shared.DataFlow.ValueSet; namespace Mono.Linker.Dataflow { @@ -15,14 +19,26 @@ namespace Mono.Linker.Dataflow /// readonly struct StackSlot { - public ValueNode? Value { get; } + public MultiValue Value { get; } /// /// True if the value is on the stack as a byref /// public bool IsByRef { get; } - public StackSlot (ValueNode? value, bool isByRef = false) + public StackSlot () + { + Value = new MultiValue (UnknownValue.Instance); + IsByRef = false; + } + + public StackSlot (SingleValue value, bool isByRef = false) + { + Value = new MultiValue (value); + IsByRef = isByRef; + } + + public StackSlot (MultiValue value, bool isByRef = false) { Value = value; IsByRef = isByRef; @@ -32,13 +48,14 @@ public StackSlot (ValueNode? value, bool isByRef = false) abstract partial class MethodBodyScanner { protected readonly LinkContext _context; + protected static ValueSetLattice MultiValueLattice => default; protected MethodBodyScanner (LinkContext context) { this._context = context; } - internal ValueNode? MethodReturnValue { private set; get; } + internal MultiValue ReturnValue { private set; get; } protected virtual void WarnAboutInvalidILInMethod (MethodBody method, int ilOffset) { @@ -83,16 +100,7 @@ private StackSlot PopUnknown (Stack stack, int count, MethodBody meth private static StackSlot MergeStackElement (StackSlot a, StackSlot b) { - StackSlot mergedSlot; - if (b.Value == null) { - mergedSlot = a; - } else if (a.Value == null) { - mergedSlot = b; - } else { - mergedSlot = new StackSlot (MergePointValue.MergeValues (a.Value, b.Value)); - } - - return mergedSlot; + return new StackSlot (MultiValueLattice.Meet (a.Value, b.Value)); } // Merge stacks together. This may return the first stack, the stack length must be the same for the two stacks. @@ -174,31 +182,28 @@ public int MoveNext (Instruction op) private static void StoreMethodLocalValue ( Dictionary valueCollection, - ValueNode? valueToStore, + in MultiValue valueToStore, KeyType collectionKey, int curBasicBlock, int? maxTrackedValues = null) where KeyType : notnull { - ValueBasicBlockPair newValue = new ValueBasicBlockPair { BasicBlockIndex = curBasicBlock }; - - ValueBasicBlockPair existingValue; - if (valueCollection.TryGetValue (collectionKey, out existingValue)) { + if (valueCollection.TryGetValue (collectionKey, out ValueBasicBlockPair existingValue)) { + MultiValue value; if (existingValue.BasicBlockIndex == curBasicBlock) { - // If the previous value was stored in the current basic block, then we can safely + // If the previous value was stored in the current basic block, then we can safely // overwrite the previous value with the new one. - newValue.Value = valueToStore; + value = valueToStore; } else { - // If the previous value came from a previous basic block, then some other use of - // the local could see the previous value, so we must merge the new value with the + // If the previous value came from a previous basic block, then some other use of + // the local could see the previous value, so we must merge the new value with the // old value. - newValue.Value = MergePointValue.MergeValues (existingValue.Value, valueToStore); + value = MultiValueLattice.Meet (existingValue.Value, valueToStore); } - valueCollection[collectionKey] = newValue; + valueCollection[collectionKey] = new ValueBasicBlockPair (value, curBasicBlock); } else if (maxTrackedValues == null || valueCollection.Count < maxTrackedValues) { // We're not currently tracking a value a this index, so store the value now. - newValue.Value = valueToStore; - valueCollection[collectionKey] = newValue; + valueCollection[collectionKey] = new ValueBasicBlockPair (valueToStore, curBasicBlock); } } @@ -215,7 +220,7 @@ public void Scan (MethodBody methodBody) BasicBlockIterator blockIterator = new BasicBlockIterator (methodBody); - MethodReturnValue = null; + ReturnValue = new (); foreach (Instruction operation in methodBody.Instructions) { int curBasicBlock = blockIterator.MoveNext (operation); @@ -421,12 +426,12 @@ public void Scan (MethodBody methodBody) case Code.Ldsfld: case Code.Ldflda: case Code.Ldsflda: - ScanLdfld (operation, currentStack, thisMethod, methodBody); + ScanLdfld (operation, currentStack, methodBody); break; case Code.Newarr: { StackSlot count = PopUnknown (currentStack, 1, methodBody, operation.Offset); - currentStack.Push (new StackSlot (new ArrayValue (count.Value, (TypeReference) operation.Operand))); + currentStack.Push (new StackSlot (ArrayValue.Create (count.Value, (TypeReference) operation.Operand))); } break; @@ -532,7 +537,7 @@ public void Scan (MethodBody methodBody) // Pop function pointer PopUnknown (currentStack, 1, methodBody, operation.Offset); - if (GetReturnTypeWithoutModifiers (signature.ReturnType).MetadataType != MetadataType.Void) + if (!signature.ReturnsVoid ()) PushUnknown (currentStack); } break; @@ -568,14 +573,14 @@ public void Scan (MethodBody methodBody) case Code.Ret: { - bool hasReturnValue = GetReturnTypeWithoutModifiers (methodBody.Method.ReturnType).MetadataType != MetadataType.Void; + bool hasReturnValue = !methodBody.Method.ReturnsVoid (); if (currentStack.Count != (hasReturnValue ? 1 : 0)) { WarnAboutInvalidILInMethod (methodBody, operation.Offset); } if (hasReturnValue) { StackSlot retValue = PopUnknown (currentStack, 1, methodBody, operation.Offset); - MethodReturnValue = MergePointValue.MergeValues (MethodReturnValue, retValue.Value); + ReturnValue = MultiValueLattice.Meet (ReturnValue, retValue.Value); } ClearStack (ref currentStack); break; @@ -633,7 +638,7 @@ private static void ScanExceptionInformation (Dictionary> } } - protected abstract ValueNode GetMethodParameterValue (MethodDefinition method, int parameterIndex); + protected abstract SingleValue GetMethodParameterValue (MethodDefinition method, int parameterIndex); private void ScanLdarg (Instruction operation, Stack currentStack, MethodDefinition thisMethod, MethodBody methodBody) { @@ -687,7 +692,11 @@ private void ScanStarg ( { ParameterDefinition param = (ParameterDefinition) operation.Operand; var valueToStore = PopUnknown (currentStack, 1, methodBody, operation.Offset); - HandleStoreParameter (thisMethod, param.Sequence, operation, valueToStore.Value); + var targetValue = GetMethodParameterValue (thisMethod, param.Sequence); + if (targetValue is MethodParameterValue targetParameterValue) + HandleStoreParameter (thisMethod, targetParameterValue, operation, valueToStore.Value); + + // If the targetValue is MethodThisValue do nothing - it should never happen really, and if it does, there's nothing we can track there } private void ScanLdloc ( @@ -705,41 +714,49 @@ private void ScanLdloc ( bool isByRef = operation.OpCode.Code == Code.Ldloca || operation.OpCode.Code == Code.Ldloca_S || localDef.VariableType.IsByRefOrPointer (); - ValueBasicBlockPair localValue; - locals.TryGetValue (localDef, out localValue); - if (localValue.Value != null) { - ValueNode valueToPush = localValue.Value; - currentStack.Push (new StackSlot (valueToPush, isByRef)); - } else { - currentStack.Push (new StackSlot (null, isByRef)); - } + if (!locals.TryGetValue (localDef, out ValueBasicBlockPair localValue)) + currentStack.Push (new StackSlot (UnknownValue.Instance, isByRef)); + else + currentStack.Push (new StackSlot (localValue.Value, isByRef)); } void ScanLdtoken (Instruction operation, Stack currentStack) { - if (operation.Operand is GenericParameter genericParameter) { - StackSlot slot = new StackSlot (new RuntimeTypeHandleForGenericParameterValue (genericParameter)); - currentStack.Push (slot); + switch (operation.Operand) { + case GenericParameter genericParameter: + var param = new RuntimeTypeHandleForGenericParameterValue (genericParameter); + currentStack.Push (new StackSlot (param)); return; - } - - if (operation.Operand is TypeReference typeReference) { - var resolvedReference = ResolveToTypeDefinition (typeReference); - if (resolvedReference != null) { - StackSlot slot = new StackSlot (new RuntimeTypeHandleValue (resolvedReference)); - currentStack.Push (slot); - return; - } - } else if (operation.Operand is MethodReference methodReference) { - var resolvedMethod = _context.TryResolve (methodReference); - if (resolvedMethod != null) { - StackSlot slot = new StackSlot (new RuntimeMethodHandleValue (resolvedMethod)); - currentStack.Push (slot); + case TypeReference typeReference when ResolveToTypeDefinition (typeReference) is TypeDefinition resolvedDefinition: + // Note that Nullable types without a generic argument (i.e. Nullable<>) will be RuntimeTypeHandleValue / SystemTypeValue + if (typeReference is IGenericInstance instance && resolvedDefinition.IsTypeOf (WellKnownType.System_Nullable_T)) { + switch (instance.GenericArguments[0]) { + case GenericParameter genericParam: + var nullableDam = new RuntimeTypeHandleForNullableValueWithDynamicallyAccessedMembers (new TypeProxy (resolvedDefinition), + new RuntimeTypeHandleForGenericParameterValue (genericParam)); + currentStack.Push (new StackSlot (nullableDam)); + return; + case TypeReference underlyingTypeReference when ResolveToTypeDefinition (underlyingTypeReference) is TypeDefinition underlyingType: + var nullableType = new RuntimeTypeHandleForNullableSystemTypeValue (new TypeProxy (resolvedDefinition), new SystemTypeValue (underlyingType)); + currentStack.Push (new StackSlot (nullableType)); + return; + default: + PushUnknown (currentStack); + return; + } + } else { + var typeHandle = new RuntimeTypeHandleValue (new TypeProxy (resolvedDefinition)); + currentStack.Push (new StackSlot (typeHandle)); return; } + case MethodReference methodReference when _context.TryResolve (methodReference) is MethodDefinition resolvedMethod: + var method = new RuntimeMethodHandleValue (resolvedMethod); + currentStack.Push (new StackSlot (method)); + return; + default: + PushUnknown (currentStack); + return; } - - PushUnknown (currentStack); } private void ScanStloc ( @@ -767,22 +784,21 @@ private void ScanIndirectStore ( StackSlot valueToStore = PopUnknown (currentStack, 1, methodBody, operation.Offset); StackSlot destination = PopUnknown (currentStack, 1, methodBody, operation.Offset); - foreach (var uniqueDestination in destination.Value.UniqueValues ()) { - if (uniqueDestination.Kind == ValueNodeKind.LoadField) { - HandleStoreField (methodBody.Method, ((LoadFieldValue) uniqueDestination).Field, operation, valueToStore.Value); - } else if (uniqueDestination.Kind == ValueNodeKind.MethodParameter) { - HandleStoreParameter (methodBody.Method, ((MethodParameterValue) uniqueDestination).ParameterIndex, operation, valueToStore.Value); + foreach (var uniqueDestination in destination.Value) { + if (uniqueDestination is FieldValue fieldDestination) { + HandleStoreField (methodBody.Method, fieldDestination, operation, valueToStore.Value); + } else if (uniqueDestination is MethodParameterValue parameterDestination) { + HandleStoreParameter (methodBody.Method, parameterDestination, operation, valueToStore.Value); } } } - protected abstract ValueNode GetFieldValue (MethodDefinition method, FieldDefinition field); + protected abstract MultiValue GetFieldValue (FieldDefinition field); private void ScanLdfld ( Instruction operation, Stack currentStack, - MethodDefinition thisMethod, MethodBody methodBody) { Code code = operation.OpCode.Code; @@ -793,7 +809,7 @@ private void ScanLdfld ( FieldDefinition? field = _context.TryResolve ((FieldReference) operation.Operand); if (field != null) { - StackSlot slot = new StackSlot (GetFieldValue (thisMethod, field), isByRef); + StackSlot slot = new StackSlot (GetFieldValue (field), isByRef); currentStack.Push (slot); return; } @@ -801,11 +817,11 @@ private void ScanLdfld ( PushUnknown (currentStack); } - protected virtual void HandleStoreField (MethodDefinition method, FieldDefinition field, Instruction operation, ValueNode? valueToStore) + protected virtual void HandleStoreField (MethodDefinition method, FieldValue field, Instruction operation, MultiValue valueToStore) { } - protected virtual void HandleStoreParameter (MethodDefinition method, int index, Instruction operation, ValueNode? valueToStore) + protected virtual void HandleStoreParameter (MethodDefinition method, MethodParameterValue parameter, Instruction operation, MultiValue valueToStore) { } @@ -821,7 +837,14 @@ private void ScanStfld ( FieldDefinition? field = _context.TryResolve ((FieldReference) operation.Operand); if (field != null) { - HandleStoreField (thisMethod, field, operation, valueToStoreSlot.Value); + foreach (var value in GetFieldValue (field)) { + // GetFieldValue may return different node types, in which case they can't be stored to. + // At least not yet. + if (value is not FieldValue fieldValue) + continue; + + HandleStoreField (thisMethod, fieldValue, operation, valueToStoreSlot.Value); + } } } @@ -841,7 +864,7 @@ private ValueNodeList PopCallArguments ( MethodReference methodCalled, MethodBody containingMethodBody, bool isNewObj, int ilOffset, - out ValueNode? newObjValue) + out SingleValue? newObjValue) { newObjValue = null; @@ -874,11 +897,11 @@ private void HandleCall ( bool isNewObj = operation.OpCode.Code == Code.Newobj; - ValueNode? newObjValue; + SingleValue? newObjValue; ValueNodeList methodParams = PopCallArguments (currentStack, calledMethod, callingMethodBody, isNewObj, operation.Offset, out newObjValue); - ValueNode? methodReturnValue; + MultiValue methodReturnValue; bool handledFunction = HandleCall ( callingMethodBody, calledMethod, @@ -890,51 +913,36 @@ private void HandleCall ( if (!handledFunction) { if (isNewObj) { if (newObjValue == null) - PushUnknown (currentStack); + methodReturnValue = new MultiValue (UnknownValue.Instance); else methodReturnValue = newObjValue; } else { - if (GetReturnTypeWithoutModifiers (calledMethod.ReturnType).MetadataType != MetadataType.Void) { + if (!calledMethod.ReturnsVoid ()) { methodReturnValue = UnknownValue.Instance; } } } - if (methodReturnValue != null) + if (isNewObj || !calledMethod.ReturnsVoid ()) currentStack.Push (new StackSlot (methodReturnValue, calledMethod.ReturnType.IsByRefOrPointer ())); foreach (var param in methodParams) { - if (param is ArrayValue arr) { - MarkArrayValuesAsUnknown (arr, curBasicBlock); + foreach (var v in param) { + if (v is ArrayValue arr) { + MarkArrayValuesAsUnknown (arr, curBasicBlock); + } } } } - protected static TypeReference GetReturnTypeWithoutModifiers (TypeReference returnType) - { - while (returnType is IModifierType) { - returnType = ((IModifierType) returnType).ElementType; - } - return returnType; - } - - // Array types that are dynamically accessed should resolve to System.Array instead of its element type - which is what Cecil resolves to. - // Any data flow annotations placed on a type parameter which receives an array type apply to the array itself. None of the members in its - // element type should be marked. - public TypeDefinition? ResolveToTypeDefinition (TypeReference typeReference) - { - if (typeReference is ArrayType) - return BCL.FindPredefinedType ("System", "Array", _context); - - return _context.TryResolve (typeReference); - } + public TypeDefinition? ResolveToTypeDefinition (TypeReference typeReference) => typeReference.ResolveToTypeDefinition (_context); public abstract bool HandleCall ( MethodBody callingMethodBody, MethodReference calledMethod, Instruction operation, ValueNodeList methodParams, - out ValueNode? methodReturnValue); + out MultiValue methodReturnValue); // Limit tracking array values to 32 values for performance reasons. There are many arrays much longer than 32 elements in .NET, but the interesting ones for the linker are nearly always less than 32 elements. private const int MaxTrackedArrayValues = 32; @@ -959,7 +967,7 @@ private void ScanStelem ( StackSlot indexToStoreAt = PopUnknown (currentStack, 1, methodBody, operation.Offset); StackSlot arrayToStoreIn = PopUnknown (currentStack, 1, methodBody, operation.Offset); int? indexToStoreAtInt = indexToStoreAt.Value.AsConstInt (); - foreach (var array in arrayToStoreIn.Value.UniqueValues ()) { + foreach (var array in arrayToStoreIn.Value) { if (array is ArrayValue arrValue) { if (indexToStoreAtInt == null) { MarkArrayValuesAsUnknown (arrValue, curBasicBlock); @@ -979,7 +987,7 @@ private void ScanLdelem ( { StackSlot indexToLoadFrom = PopUnknown (currentStack, 1, methodBody, operation.Offset); StackSlot arrayToLoadFrom = PopUnknown (currentStack, 1, methodBody, operation.Offset); - if (arrayToLoadFrom.Value is not ArrayValue arr) { + if (arrayToLoadFrom.Value.AsSingleValue () is not ArrayValue arr) { PushUnknown (currentStack); return; } @@ -994,15 +1002,10 @@ private void ScanLdelem ( return; } - - ValueBasicBlockPair arrayIndexValue; - arr.IndexValues.TryGetValue (index.Value, out arrayIndexValue); - if (arrayIndexValue.Value != null) { - ValueNode valueToPush = arrayIndexValue.Value; - currentStack.Push (new StackSlot (valueToPush, isByRef)); - } else { - currentStack.Push (new StackSlot (null, isByRef)); - } + if (arr.IndexValues.TryGetValue (index.Value, out ValueBasicBlockPair arrayIndexValue)) + currentStack.Push (new StackSlot (arrayIndexValue.Value, isByRef)); + else + PushUnknown (currentStack); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodParameterValue.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodParameterValue.cs new file mode 100644 index 0000000000000..325b028527396 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodParameterValue.cs @@ -0,0 +1,49 @@ +// 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.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using ILLink.Shared.DataFlow; +using Mono.Cecil; +using Mono.Linker.Dataflow; +using TypeDefinition = Mono.Cecil.TypeDefinition; + + +namespace ILLink.Shared.TrimAnalysis +{ + + /// + /// A value that came from a method parameter - such as the result of a ldarg. + /// + partial record MethodParameterValue : IValueWithStaticType + { + public MethodParameterValue (TypeDefinition? staticType, MethodDefinition method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + StaticType = staticType; + Method = method; + ParameterIndex = parameterIndex; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public readonly MethodDefinition Method; + + /// + /// This is the index of non-implicit parameter - so the index into MethodDefinition.Parameters array. + /// It's NOT the IL parameter index which could be offset by 1 if the method has an implicit this. + /// + public readonly int ParameterIndex; + + public ParameterDefinition ParameterDefinition => Method.Parameters[ParameterIndex]; + + public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } + + public override IEnumerable GetDiagnosticArgumentsForAnnotationMismatch () + => new string[] { DiagnosticUtilities.GetParameterNameForErrorMessage (ParameterDefinition), DiagnosticUtilities.GetMethodSignatureDisplayName (Method) }; + + public TypeDefinition? StaticType { get; } + + public override SingleValue DeepCopy () => this; // This value is immutable + + public override string ToString () => this.ValueToString (Method, ParameterIndex, DynamicallyAccessedMemberTypes); + } +} \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodProxy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodProxy.cs new file mode 100644 index 0000000000000..6180b2bdb4f20 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodProxy.cs @@ -0,0 +1,55 @@ +// 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.Collections.Immutable; +using Mono.Cecil; +using Mono.Linker; + +namespace ILLink.Shared.TypeSystemProxy +{ + readonly partial struct MethodProxy + { + public MethodProxy (MethodDefinition method) => Method = method; + + public static implicit operator MethodProxy (MethodDefinition method) => new (method); + + public readonly MethodDefinition Method; + + public string Name { get => Method.Name; } + + public string GetDisplayName () => Method.GetDisplayName (); + + internal partial bool IsDeclaredOnType (string fullTypeName) => Method.IsDeclaredOnType (fullTypeName); + + internal partial bool HasParameters () => Method.HasParameters; + + internal partial int GetParametersCount () => Method.Parameters.Count; + + internal partial bool HasParameterOfType (int parameterIndex, string fullTypeName) => Method.HasParameterOfType (parameterIndex, fullTypeName); + + internal partial string GetParameterDisplayName (int parameterIndex) => Method.Parameters[parameterIndex].Name; + + internal partial bool HasGenericParameters () => Method.HasGenericParameters; + + internal partial bool HasGenericParametersCount (int genericParameterCount) => Method.GenericParameters.Count == genericParameterCount; + + internal partial ImmutableArray GetGenericParameters () + { + if (!Method.HasGenericParameters) + return ImmutableArray.Empty; + + var builder = ImmutableArray.CreateBuilder (Method.GenericParameters.Count); + foreach (var genericParameter in Method.GenericParameters) { + builder.Add (new GenericParameterProxy (genericParameter)); + } + + return builder.ToImmutableArray (); + } + + internal partial bool IsStatic () => Method.IsStatic; + + internal partial bool ReturnsVoid () => Method.ReturnsVoid (); + + public override string ToString () => Method.ToString (); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodReturnValue.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodReturnValue.cs new file mode 100644 index 0000000000000..f6cb423c121e7 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodReturnValue.cs @@ -0,0 +1,39 @@ +// 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.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using ILLink.Shared.DataFlow; +using Mono.Cecil; +using Mono.Linker.Dataflow; +using TypeDefinition = Mono.Cecil.TypeDefinition; + + +namespace ILLink.Shared.TrimAnalysis +{ + /// + /// Return value from a method + /// + partial record MethodReturnValue : IValueWithStaticType + { + public MethodReturnValue (TypeDefinition? staticType, MethodDefinition method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + StaticType = staticType; + Method = method; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public readonly MethodDefinition Method; + + public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } + + public override IEnumerable GetDiagnosticArgumentsForAnnotationMismatch () + => new string[] { DiagnosticUtilities.GetMethodSignatureDisplayName (Method) }; + + public TypeDefinition? StaticType { get; } + + public override SingleValue DeepCopy () => this; // This value is immutable + + public override string ToString () => this.ValueToString (Method, DynamicallyAccessedMemberTypes); + } +} \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodThisParameterValue.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodThisParameterValue.cs new file mode 100644 index 0000000000000..c2f46d5996e1f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/MethodThisParameterValue.cs @@ -0,0 +1,40 @@ +// 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.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using ILLink.Shared.DataFlow; +using Mono.Cecil; +using Mono.Linker; +using Mono.Linker.Dataflow; +using TypeDefinition = Mono.Cecil.TypeDefinition; + + +namespace ILLink.Shared.TrimAnalysis +{ + + /// + /// A value that came from the implicit this parameter of a method + /// + partial record MethodThisParameterValue : IValueWithStaticType + { + public MethodThisParameterValue (MethodDefinition method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + Method = method; + DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; + } + + public readonly MethodDefinition Method; + + public override DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; } + + public override IEnumerable GetDiagnosticArgumentsForAnnotationMismatch () + => new string[] { Method.GetDisplayName () }; + + public TypeDefinition? StaticType => Method.DeclaringType; + + public override SingleValue DeepCopy () => this; // This value is immutable + + public override string ToString () => this.ValueToString (Method, DynamicallyAccessedMemberTypes); + } +} \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/README.md b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/README.md index 693097ed774ad..0fb01d85ff398 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/README.md +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/README.md @@ -1 +1 @@ -Sources taken from https://github.com/dotnet/linker/tree/9996319f2a619c2911b02c164b4d6b1d20e29a39/src/linker/Linker.Dataflow. +Sources taken from https://github.com/dotnet/linker/tree/9eb23e4a61c59b8dd12078e50fce3147b41171da/src/linker/Linker.Dataflow. diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionMarker.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionMarker.cs new file mode 100644 index 0000000000000..51268fdfac854 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionMarker.cs @@ -0,0 +1,123 @@ +// 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.Cecil; +using Mono.Linker.Steps; + +namespace Mono.Linker.Dataflow +{ + public readonly struct ReflectionMarker + { + readonly LinkContext _context; + readonly MarkStep _markStep; + + public ReflectionMarker (LinkContext context, MarkStep markStep) + { + _context = context; + _markStep = markStep; + } + + internal void MarkTypeForDynamicallyAccessedMembers (in MessageOrigin origin, TypeDefinition typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes, DependencyKind dependencyKind, bool declaredOnly = false) + { + foreach (var member in typeDefinition.GetDynamicallyAccessedMembers (_context, requiredMemberTypes, declaredOnly)) { + switch (member) { + case MethodDefinition method: + MarkMethod (origin, method, dependencyKind); + break; + case FieldDefinition field: + MarkField (origin, field, dependencyKind); + break; + case TypeDefinition nestedType: + MarkType (origin, nestedType, dependencyKind); + break; + case PropertyDefinition property: + MarkProperty (origin, property, dependencyKind); + break; + case EventDefinition @event: + MarkEvent (origin, @event, dependencyKind); + break; + case InterfaceImplementation interfaceImplementation: + MarkInterfaceImplementation (origin, interfaceImplementation, dependencyKind); + break; + } + } + } + + internal bool TryResolveTypeNameAndMark (string typeName, MessageOrigin origin, bool needsAssemblyName, [NotNullWhen (true)] out TypeDefinition? type) + { + if (!_context.TypeNameResolver.TryResolveTypeName (typeName, origin.Provider, out TypeReference? typeRef, out AssemblyDefinition? typeAssembly, needsAssemblyName) + || typeRef.ResolveToTypeDefinition (_context) is not TypeDefinition foundType) { + type = default; + return false; + } + + _markStep.MarkTypeVisibleToReflection (typeRef, foundType, new DependencyInfo (DependencyKind.AccessedViaReflection, origin.Provider), origin); + _context.MarkingHelpers.MarkMatchingExportedType (foundType, typeAssembly, new DependencyInfo (DependencyKind.DynamicallyAccessedMember, foundType), origin); + + type = foundType; + return true; + } + + internal void MarkType (in MessageOrigin origin, TypeDefinition type, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + { + _markStep.MarkTypeVisibleToReflection (type, type, new DependencyInfo (dependencyKind, origin.Provider), origin); + } + + internal void MarkMethod (in MessageOrigin origin, MethodDefinition method, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + { + _markStep.MarkMethodVisibleToReflection (method, new DependencyInfo (dependencyKind, origin.Provider), origin); + } + + void MarkField (in MessageOrigin origin, FieldDefinition field, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + { + _markStep.MarkFieldVisibleToReflection (field, new DependencyInfo (dependencyKind, origin.Provider), origin); + } + + internal void MarkProperty (in MessageOrigin origin, PropertyDefinition property, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + { + _markStep.MarkPropertyVisibleToReflection (property, new DependencyInfo (dependencyKind, origin.Provider), origin); + } + + void MarkEvent (in MessageOrigin origin, EventDefinition @event, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + { + _markStep.MarkEventVisibleToReflection (@event, new DependencyInfo (dependencyKind, origin.Provider), origin); + } + + void MarkInterfaceImplementation (in MessageOrigin origin, InterfaceImplementation interfaceImplementation, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) + { + _markStep.MarkInterfaceImplementation (interfaceImplementation, null, new DependencyInfo (dependencyKind, origin.Provider)); + } + + internal void MarkConstructorsOnType (in MessageOrigin origin, TypeDefinition type, Func? filter, BindingFlags? bindingFlags = null) + { + foreach (var ctor in type.GetConstructorsOnType (filter, bindingFlags)) + MarkMethod (origin, ctor); + } + + internal void MarkFieldsOnTypeHierarchy (in MessageOrigin origin, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + foreach (var field in type.GetFieldsOnTypeHierarchy (_context, filter, bindingFlags)) + MarkField (origin, field); + } + + internal void MarkPropertiesOnTypeHierarchy (in MessageOrigin origin, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + foreach (var property in type.GetPropertiesOnTypeHierarchy (_context, filter, bindingFlags)) + MarkProperty (origin, property); + } + + internal void MarkEventsOnTypeHierarchy (in MessageOrigin origin, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) + { + foreach (var @event in type.GetEventsOnTypeHierarchy (_context, filter, bindingFlags)) + MarkEvent (origin, @event); + } + + internal void MarkStaticConstructor (in MessageOrigin origin, TypeDefinition type) + { + _markStep.MarkStaticConstructorVisibleToReflection (type, new DependencyInfo (DependencyKind.AccessedViaReflection, origin.Provider), origin); + } + } +} \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionMethodBodyScanner.cs index 67ce51fe05088..ee9cd02264cb3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionMethodBodyScanner.cs @@ -1,35 +1,29 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// 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.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using ILLink.Shared; +using ILLink.Shared.DataFlow; +using ILLink.Shared.TrimAnalysis; +using ILLink.Shared.TypeSystemProxy; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Linker.Steps; - -using BindingFlags = System.Reflection.BindingFlags; +using MultiValue = ILLink.Shared.DataFlow.ValueSet; namespace Mono.Linker.Dataflow { class ReflectionMethodBodyScanner : MethodBodyScanner { readonly MarkStep _markStep; - readonly MarkScopeStack _scopeStack; - static readonly DynamicallyAccessedMemberTypes[] AllDynamicallyAccessedMemberTypes = GetAllDynamicallyAccessedMemberTypes (); - - static DynamicallyAccessedMemberTypes[] GetAllDynamicallyAccessedMemberTypes () - { - HashSet values = Enum.GetValues (typeof (DynamicallyAccessedMemberTypes)) - .Cast () - .ToHashSet (); - if (!values.Contains (DynamicallyAccessedMemberTypesOverlay.Interfaces)) - values.Add (DynamicallyAccessedMemberTypesOverlay.Interfaces); - return values.ToArray (); - } + MessageOrigin _origin; + readonly FlowAnnotations _annotations; + readonly ReflectionMarker _reflectionMarker; public static bool RequiresReflectionMethodBodyScannerForCallSite (LinkContext context, MethodReference calledMethod) { @@ -37,17 +31,16 @@ public static bool RequiresReflectionMethodBodyScannerForCallSite (LinkContext c if (methodDefinition == null) return false; - return GetIntrinsicIdForMethod (methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || + return Intrinsics.GetIntrinsicIdForMethod (methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (methodDefinition) || context.Annotations.DoesMethodRequireUnreferencedCode (methodDefinition, out _) || methodDefinition.IsPInvokeImpl; } - public static bool RequiresReflectionMethodBodyScannerForMethodBody (FlowAnnotations flowAnnotations, MethodDefinition methodDefinition) + public static bool RequiresReflectionMethodBodyScannerForMethodBody (LinkContext context, MethodDefinition methodDefinition) { - return - GetIntrinsicIdForMethod (methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || - flowAnnotations.RequiresDataFlowAnalysis (methodDefinition); + return Intrinsics.GetIntrinsicIdForMethod (methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || + context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (methodDefinition); } public static bool RequiresReflectionMethodBodyScannerForAccess (LinkContext context, FieldReference field) @@ -59,116 +52,117 @@ public static bool RequiresReflectionMethodBodyScannerForAccess (LinkContext con return context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (fieldDefinition); } - bool ShouldEnableReflectionPatternReporting () + bool ShouldEnableReflectionPatternReporting (ICustomAttributeProvider? provider) { - if (_markStep.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode ()) + if (_markStep.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (provider)) return false; return true; } - public ReflectionMethodBodyScanner (LinkContext context, MarkStep parent, MarkScopeStack scopeStack) + public ReflectionMethodBodyScanner (LinkContext context, MarkStep parent, MessageOrigin origin) : base (context) { _markStep = parent; - _scopeStack = scopeStack; + _origin = origin; + _annotations = context.Annotations.FlowAnnotations; + _reflectionMarker = new ReflectionMarker (context, parent); } public void ScanAndProcessReturnValue (MethodBody methodBody) { Scan (methodBody); - if (GetReturnTypeWithoutModifiers (methodBody.Method.ReturnType).MetadataType != MetadataType.Void) { + if (!methodBody.Method.ReturnsVoid ()) { var method = methodBody.Method; - var requiredMemberTypes = _context.Annotations.FlowAnnotations.GetReturnParameterAnnotation (method); - if (requiredMemberTypes != 0) { - var reflectionContext = new ReflectionPatternContext (_context, ShouldEnableReflectionPatternReporting (), _scopeStack.CurrentScope.Origin, method.MethodReturnType); - reflectionContext.AnalyzingPattern (); - RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, MethodReturnValue, method.MethodReturnType); - reflectionContext.Dispose (); + var methodReturnValue = _annotations.GetMethodReturnValue (method); + if (methodReturnValue.DynamicallyAccessedMemberTypes != 0) { + var diagnosticContext = new DiagnosticContext (_origin, ShouldEnableReflectionPatternReporting (_origin.Provider), _context); + RequireDynamicallyAccessedMembers (diagnosticContext, ReturnValue, methodReturnValue); } } } public void ProcessAttributeDataflow (MethodDefinition method, IList arguments) { - int paramOffset = method.HasImplicitThis () ? 1 : 0; - for (int i = 0; i < method.Parameters.Count; i++) { - var annotation = _context.Annotations.FlowAnnotations.GetParameterAnnotation (method, i + paramOffset); - if (annotation != DynamicallyAccessedMemberTypes.None) { - ValueNode valueNode = GetValueNodeForCustomAttributeArgument (arguments[i]); - var methodParameter = method.Parameters[i]; - var reflectionContext = new ReflectionPatternContext (_context, true, _scopeStack.CurrentScope.Origin, methodParameter); - reflectionContext.AnalyzingPattern (); - RequireDynamicallyAccessedMembers (ref reflectionContext, annotation, valueNode, methodParameter); - reflectionContext.Dispose (); + var parameterValue = _annotations.GetMethodParameterValue (method, i); + if (parameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None) { + MultiValue value = GetValueNodeForCustomAttributeArgument (arguments[i]); + var diagnosticContext = new DiagnosticContext (_origin, diagnosticsEnabled: true, _context); + RequireDynamicallyAccessedMembers (diagnosticContext, value, parameterValue); } } } public void ProcessAttributeDataflow (FieldDefinition field, CustomAttributeArgument value) { - var annotation = _context.Annotations.FlowAnnotations.GetFieldAnnotation (field); - Debug.Assert (annotation != DynamicallyAccessedMemberTypes.None); + MultiValue valueNode = GetValueNodeForCustomAttributeArgument (value); + foreach (var fieldValueCandidate in GetFieldValue (field)) { + if (fieldValueCandidate is not ValueWithDynamicallyAccessedMembers fieldValue) + continue; - ValueNode valueNode = GetValueNodeForCustomAttributeArgument (value); - var reflectionContext = new ReflectionPatternContext (_context, true, _scopeStack.CurrentScope.Origin, field); - reflectionContext.AnalyzingPattern (); - RequireDynamicallyAccessedMembers (ref reflectionContext, annotation, valueNode, field); - reflectionContext.Dispose (); + var diagnosticContext = new DiagnosticContext (_origin, diagnosticsEnabled: true, _context); + RequireDynamicallyAccessedMembers (diagnosticContext, valueNode, fieldValue); + } } - ValueNode GetValueNodeForCustomAttributeArgument (CustomAttributeArgument argument) + MultiValue GetValueNodeForCustomAttributeArgument (CustomAttributeArgument argument) { - ValueNode valueNode; + SingleValue value; if (argument.Type.Name == "Type") { TypeDefinition? referencedType = ResolveToTypeDefinition ((TypeReference) argument.Value); if (referencedType == null) - valueNode = UnknownValue.Instance; + value = UnknownValue.Instance; else - valueNode = new SystemTypeValue (referencedType); + value = new SystemTypeValue (referencedType); } else if (argument.Type.MetadataType == MetadataType.String) { - valueNode = new KnownStringValue ((string) argument.Value); + value = new KnownStringValue ((string) argument.Value); } else { // We shouldn't have gotten a non-null annotation for this from GetParameterAnnotation throw new InvalidOperationException (); } - Debug.Assert (valueNode != null); - return valueNode; + Debug.Assert (value != null); + return value; } public void ProcessGenericArgumentDataFlow (GenericParameter genericParameter, TypeReference genericArgument) { - var annotation = _context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (genericParameter); - Debug.Assert (annotation != DynamicallyAccessedMemberTypes.None); + var genericParameterValue = _annotations.GetGenericParameterValue (genericParameter); + Debug.Assert (genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None); - ValueNode valueNode = GetTypeValueNodeFromGenericArgument (genericArgument); - var currentScopeOrigin = _scopeStack.CurrentScope.Origin; + MultiValue genericArgumentValue = GetTypeValueNodeFromGenericArgument (genericArgument); - var reflectionContext = new ReflectionPatternContext (_context, ShouldEnableReflectionPatternReporting (), currentScopeOrigin, genericParameter); - reflectionContext.AnalyzingPattern (); - RequireDynamicallyAccessedMembers (ref reflectionContext, annotation, valueNode, genericParameter); - reflectionContext.Dispose (); + var diagnosticContext = new DiagnosticContext (_origin, ShouldEnableReflectionPatternReporting (_origin.Provider), _context); + RequireDynamicallyAccessedMembers (diagnosticContext, genericArgumentValue, genericParameterValue); } - ValueNode GetTypeValueNodeFromGenericArgument (TypeReference genericArgument) + MultiValue GetTypeValueNodeFromGenericArgument (TypeReference genericArgument) { if (genericArgument is GenericParameter inputGenericParameter) { // Technically this should be a new value node type as it's not a System.Type instance representation, but just the generic parameter // That said we only use it to perform the dynamically accessed members checks and for that purpose treating it as System.Type is perfectly valid. - return new SystemTypeForGenericParameterValue (inputGenericParameter, _context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (inputGenericParameter)); - } else { - TypeDefinition? genericArgumentTypeDef = ResolveToTypeDefinition (genericArgument); - if (genericArgumentTypeDef != null) { - return new SystemTypeValue (genericArgumentTypeDef); - } else { - // If we can't resolve the generic argument, it means we can't apply potential requirements on it - // so track it as unknown value. If we later on hit this unknown value as being used somewhere - // where we need to apply requirements on it, it will generate a warning. - return UnknownValue.Instance; + return _annotations.GetGenericParameterValue (inputGenericParameter); + } else if (ResolveToTypeDefinition (genericArgument) is TypeDefinition genericArgumentType) { + if (genericArgumentType.IsTypeOf (WellKnownType.System_Nullable_T)) { + var innerGenericArgument = (genericArgument as IGenericInstance)?.GenericArguments.FirstOrDefault (); + switch (innerGenericArgument) { + case GenericParameter gp: + return new NullableValueWithDynamicallyAccessedMembers (genericArgumentType, + new GenericParameterValue (gp, _context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (gp))); + + case TypeReference underlyingType: + if (ResolveToTypeDefinition (underlyingType) is TypeDefinition underlyingTypeDefinition) + return new NullableSystemTypeValue (genericArgumentType, new SystemTypeValue (underlyingTypeDefinition)); + else + return UnknownValue.Instance; + } } + // All values except for Nullable, including Nullable<> (with no type arguments) + return new SystemTypeValue (genericArgumentType); + } else { + return UnknownValue.Instance; } } @@ -183,453 +177,60 @@ protected override void WarnAboutInvalidILInMethod (MethodBody method, int ilOff Debug.Fail ("Invalid IL or a bug in the scanner"); } - MethodReturnValue CreateMethodReturnValue (MethodReference method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes = DynamicallyAccessedMemberTypes.None) - { - return new MethodReturnValue (ResolveToTypeDefinition (method.ReturnType), dynamicallyAccessedMemberTypes, method.MethodReturnType); - } + protected override ValueWithDynamicallyAccessedMembers GetMethodParameterValue (MethodDefinition method, int parameterIndex) + => GetMethodParameterValue (method, parameterIndex, _context.Annotations.FlowAnnotations.GetParameterAnnotation (method, parameterIndex)); - protected override ValueNode GetMethodParameterValue (MethodDefinition method, int parameterIndex) + ValueWithDynamicallyAccessedMembers GetMethodParameterValue (MethodDefinition method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) { - DynamicallyAccessedMemberTypes memberTypes = _context.Annotations.FlowAnnotations.GetParameterAnnotation (method, parameterIndex); + if (method.HasImplicitThis ()) { + if (parameterIndex == 0) + return _annotations.GetMethodThisParameterValue (method, dynamicallyAccessedMemberTypes); - var staticType = - !method.HasImplicitThis () ? ResolveToTypeDefinition (method.Parameters[parameterIndex].ParameterType) : - parameterIndex == 0 ? method.DeclaringType : - ResolveToTypeDefinition (method.Parameters[parameterIndex - 1].ParameterType); + parameterIndex--; + } - return new MethodParameterValue (staticType, parameterIndex, memberTypes, DiagnosticUtilities.GetMethodParameterFromIndex (method, parameterIndex)); + return _annotations.GetMethodParameterValue (method, parameterIndex, dynamicallyAccessedMemberTypes); } - protected override ValueNode GetFieldValue (MethodDefinition method, FieldDefinition field) + protected override MultiValue GetFieldValue (FieldDefinition field) { switch (field.Name) { - case "EmptyTypes" when field.DeclaringType.IsTypeOf ("System", "Type"): { - return new ArrayValue (new ConstIntValue (0), field.DeclaringType); + case "EmptyTypes" when field.DeclaringType.IsTypeOf (WellKnownType.System_Type): { + return ArrayValue.Create (0, field.DeclaringType); } - case "Empty" when field.DeclaringType.IsTypeOf ("System", "String"): { + case "Empty" when field.DeclaringType.IsTypeOf (WellKnownType.System_String): { return new KnownStringValue (string.Empty); } default: { DynamicallyAccessedMemberTypes memberTypes = _context.Annotations.FlowAnnotations.GetFieldAnnotation (field); - return new LoadFieldValue (ResolveToTypeDefinition (field.FieldType), field, memberTypes); + return new FieldValue (ResolveToTypeDefinition (field.FieldType), field, memberTypes); } } } - protected override void HandleStoreField (MethodDefinition method, FieldDefinition field, Instruction operation, ValueNode? valueToStore) + protected override void HandleStoreField (MethodDefinition method, FieldValue field, Instruction operation, MultiValue valueToStore) { - var requiredMemberTypes = _context.Annotations.FlowAnnotations.GetFieldAnnotation (field); - if (requiredMemberTypes != 0) { - _scopeStack.UpdateCurrentScopeInstructionOffset (operation.Offset); - var reflectionContext = new ReflectionPatternContext (_context, ShouldEnableReflectionPatternReporting (), _scopeStack.CurrentScope.Origin, field, operation); - reflectionContext.AnalyzingPattern (); - RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, valueToStore, field); - reflectionContext.Dispose (); + if (field.DynamicallyAccessedMemberTypes != 0) { + _origin = _origin.WithInstructionOffset (operation.Offset); + var diagnosticContext = new DiagnosticContext (_origin, ShouldEnableReflectionPatternReporting (_origin.Provider), _context); + RequireDynamicallyAccessedMembers (diagnosticContext, valueToStore, field); } } - protected override void HandleStoreParameter (MethodDefinition method, int index, Instruction operation, ValueNode? valueToStore) + protected override void HandleStoreParameter (MethodDefinition method, MethodParameterValue parameter, Instruction operation, MultiValue valueToStore) { - var requiredMemberTypes = _context.Annotations.FlowAnnotations.GetParameterAnnotation (method, index); - if (requiredMemberTypes != 0) { - ParameterDefinition parameter = method.Parameters[index - (method.HasImplicitThis () ? 1 : 0)]; - _scopeStack.UpdateCurrentScopeInstructionOffset (operation.Offset); - var reflectionContext = new ReflectionPatternContext (_context, ShouldEnableReflectionPatternReporting (), _scopeStack.CurrentScope.Origin, parameter, operation); - reflectionContext.AnalyzingPattern (); - RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, valueToStore, parameter); - reflectionContext.Dispose (); + if (parameter.DynamicallyAccessedMemberTypes != 0) { + _origin = _origin.WithInstructionOffset (operation.Offset); + var diagnosticContext = new DiagnosticContext (_origin, ShouldEnableReflectionPatternReporting (_origin.Provider), _context); + RequireDynamicallyAccessedMembers (diagnosticContext, valueToStore, parameter); } } - enum IntrinsicId - { - None = 0, - IntrospectionExtensions_GetTypeInfo, - Type_GetTypeFromHandle, - Type_get_TypeHandle, - Object_GetType, - TypeDelegator_Ctor, - Array_Empty, - TypeInfo_AsType, - MethodBase_GetMethodFromHandle, - - // Anything above this marker will require the method to be run through - // the reflection body scanner. - RequiresReflectionBodyScanner_Sentinel = 1000, - Type_MakeGenericType, - Type_GetType, - Type_GetConstructor, - Type_GetConstructors, - Type_GetMethod, - Type_GetMethods, - Type_GetField, - Type_GetFields, - Type_GetProperty, - Type_GetProperties, - Type_GetEvent, - Type_GetEvents, - Type_GetNestedType, - Type_GetNestedTypes, - Type_GetMember, - Type_GetMembers, - Type_GetInterface, - Type_get_AssemblyQualifiedName, - Type_get_UnderlyingSystemType, - Type_get_BaseType, - Expression_Call, - Expression_Field, - Expression_Property, - Expression_New, - Activator_CreateInstance_Type, - Activator_CreateInstance_AssemblyName_TypeName, - Activator_CreateInstanceFrom, - Activator_CreateInstanceOfT, - AppDomain_CreateInstance, - AppDomain_CreateInstanceAndUnwrap, - AppDomain_CreateInstanceFrom, - AppDomain_CreateInstanceFromAndUnwrap, - Assembly_CreateInstance, - RuntimeReflectionExtensions_GetRuntimeEvent, - RuntimeReflectionExtensions_GetRuntimeField, - RuntimeReflectionExtensions_GetRuntimeMethod, - RuntimeReflectionExtensions_GetRuntimeProperty, - RuntimeHelpers_RunClassConstructor, - MethodInfo_MakeGenericMethod, - } - - static IntrinsicId GetIntrinsicIdForMethod (MethodDefinition calledMethod) + public override bool HandleCall (MethodBody callingMethodBody, MethodReference calledMethod, Instruction operation, ValueNodeList methodParams, out MultiValue methodReturnValue) { - return calledMethod.Name switch { - // static System.Reflection.IntrospectionExtensions.GetTypeInfo (Type type) - "GetTypeInfo" when calledMethod.IsDeclaredOnType ("System.Reflection", "IntrospectionExtensions") => IntrinsicId.IntrospectionExtensions_GetTypeInfo, - - // System.Reflection.TypeInfo.AsType () - "AsType" when calledMethod.IsDeclaredOnType ("System.Reflection", "TypeInfo") => IntrinsicId.TypeInfo_AsType, - - // System.Type.GetTypeInfo (Type type) - "GetTypeFromHandle" when calledMethod.IsDeclaredOnType ("System", "Type") => IntrinsicId.Type_GetTypeFromHandle, - - // System.Type.GetTypeHandle (Type type) - "get_TypeHandle" when calledMethod.IsDeclaredOnType ("System", "Type") => IntrinsicId.Type_get_TypeHandle, - - // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle) - // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle, RuntimeTypeHandle declaringType) - "GetMethodFromHandle" when calledMethod.IsDeclaredOnType ("System.Reflection", "MethodBase") - && calledMethod.HasParameterOfType (0, "System", "RuntimeMethodHandle") - && (calledMethod.Parameters.Count == 1 || calledMethod.Parameters.Count == 2) - => IntrinsicId.MethodBase_GetMethodFromHandle, - - // static System.Type.MakeGenericType (Type [] typeArguments) - "MakeGenericType" when calledMethod.IsDeclaredOnType ("System", "Type") => IntrinsicId.Type_MakeGenericType, - - // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeEvent (this Type type, string name) - "GetRuntimeEvent" when calledMethod.IsDeclaredOnType ("System.Reflection", "RuntimeReflectionExtensions") - && calledMethod.HasParameterOfType (0, "System", "Type") - && calledMethod.HasParameterOfType (1, "System", "String") - => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent, - - // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeField (this Type type, string name) - "GetRuntimeField" when calledMethod.IsDeclaredOnType ("System.Reflection", "RuntimeReflectionExtensions") - && calledMethod.HasParameterOfType (0, "System", "Type") - && calledMethod.HasParameterOfType (1, "System", "String") - => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField, - - // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeMethod (this Type type, string name, Type[] parameters) - "GetRuntimeMethod" when calledMethod.IsDeclaredOnType ("System.Reflection", "RuntimeReflectionExtensions") - && calledMethod.HasParameterOfType (0, "System", "Type") - && calledMethod.HasParameterOfType (1, "System", "String") - => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod, - - // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeProperty (this Type type, string name) - "GetRuntimeProperty" when calledMethod.IsDeclaredOnType ("System.Reflection", "RuntimeReflectionExtensions") - && calledMethod.HasParameterOfType (0, "System", "Type") - && calledMethod.HasParameterOfType (1, "System", "String") - => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty, - - // static System.Linq.Expressions.Expression.Call (Type, String, Type[], Expression[]) - "Call" when calledMethod.IsDeclaredOnType ("System.Linq.Expressions", "Expression") - && calledMethod.HasParameterOfType (0, "System", "Type") - && calledMethod.Parameters.Count == 4 - => IntrinsicId.Expression_Call, - - // static System.Linq.Expressions.Expression.Field (Expression, Type, String) - "Field" when calledMethod.IsDeclaredOnType ("System.Linq.Expressions", "Expression") - && calledMethod.HasParameterOfType (1, "System", "Type") - && calledMethod.Parameters.Count == 3 - => IntrinsicId.Expression_Field, - - // static System.Linq.Expressions.Expression.Property (Expression, Type, String) - // static System.Linq.Expressions.Expression.Property (Expression, MethodInfo) - "Property" when calledMethod.IsDeclaredOnType ("System.Linq.Expressions", "Expression") - && ((calledMethod.HasParameterOfType (1, "System", "Type") && calledMethod.Parameters.Count == 3) - || (calledMethod.HasParameterOfType (1, "System.Reflection", "MethodInfo") && calledMethod.Parameters.Count == 2)) - => IntrinsicId.Expression_Property, - - // static System.Linq.Expressions.Expression.New (Type) - "New" when calledMethod.IsDeclaredOnType ("System.Linq.Expressions", "Expression") - && calledMethod.HasParameterOfType (0, "System", "Type") - && calledMethod.Parameters.Count == 1 - => IntrinsicId.Expression_New, - - // static System.Type.GetType (string) - // static System.Type.GetType (string, Boolean) - // static System.Type.GetType (string, Boolean, Boolean) - // static System.Type.GetType (string, Func, Func) - // static System.Type.GetType (string, Func, Func, Boolean) - // static System.Type.GetType (string, Func, Func, Boolean, Boolean) - "GetType" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System", "String") - => IntrinsicId.Type_GetType, - - // System.Type.GetConstructor (Type[]) - // System.Type.GetConstructor (BindingFlags, Type[]) - // System.Type.GetConstructor (BindingFlags, Binder, Type[], ParameterModifier []) - // System.Type.GetConstructor (BindingFlags, Binder, CallingConventions, Type[], ParameterModifier []) - "GetConstructor" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasThis - => IntrinsicId.Type_GetConstructor, - - // System.Type.GetConstructors (BindingFlags) - "GetConstructors" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") - && calledMethod.Parameters.Count == 1 - && calledMethod.HasThis - => IntrinsicId.Type_GetConstructors, - - // System.Type.GetMethod (string) - // System.Type.GetMethod (string, BindingFlags) - // System.Type.GetMethod (string, Type[]) - // System.Type.GetMethod (string, Type[], ParameterModifier[]) - // System.Type.GetMethod (string, BindingFlags, Type[]) - // System.Type.GetMethod (string, BindingFlags, Binder, Type[], ParameterModifier[]) - // System.Type.GetMethod (string, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[]) - // System.Type.GetMethod (string, int, Type[]) - // System.Type.GetMethod (string, int, Type[], ParameterModifier[]?) - // System.Type.GetMethod (string, int, BindingFlags, Binder?, Type[], ParameterModifier[]?) - // System.Type.GetMethod (string, int, BindingFlags, Binder?, CallingConventions, Type[], ParameterModifier[]?) - "GetMethod" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System", "String") - && calledMethod.HasThis - => IntrinsicId.Type_GetMethod, - - // System.Type.GetMethods (BindingFlags) - "GetMethods" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") - && calledMethod.Parameters.Count == 1 - && calledMethod.HasThis - => IntrinsicId.Type_GetMethods, - - // System.Type.GetField (string) - // System.Type.GetField (string, BindingFlags) - "GetField" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System", "String") - && calledMethod.HasThis - => IntrinsicId.Type_GetField, - - // System.Type.GetFields (BindingFlags) - "GetFields" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") - && calledMethod.Parameters.Count == 1 - && calledMethod.HasThis - => IntrinsicId.Type_GetFields, - - // System.Type.GetEvent (string) - // System.Type.GetEvent (string, BindingFlags) - "GetEvent" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System", "String") - && calledMethod.HasThis - => IntrinsicId.Type_GetEvent, - - // System.Type.GetEvents (BindingFlags) - "GetEvents" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") - && calledMethod.Parameters.Count == 1 - && calledMethod.HasThis - => IntrinsicId.Type_GetEvents, - - // System.Type.GetNestedType (string) - // System.Type.GetNestedType (string, BindingFlags) - "GetNestedType" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System", "String") - && calledMethod.HasThis - => IntrinsicId.Type_GetNestedType, - - // System.Type.GetNestedTypes (BindingFlags) - "GetNestedTypes" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") - && calledMethod.Parameters.Count == 1 - && calledMethod.HasThis - => IntrinsicId.Type_GetNestedTypes, - - // System.Type.GetMember (String) - // System.Type.GetMember (String, BindingFlags) - // System.Type.GetMember (String, MemberTypes, BindingFlags) - "GetMember" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System", "String") - && calledMethod.HasThis - && (calledMethod.Parameters.Count == 1 || - (calledMethod.Parameters.Count == 2 && calledMethod.HasParameterOfType (1, "System.Reflection", "BindingFlags")) || - (calledMethod.Parameters.Count == 3 && calledMethod.HasParameterOfType (2, "System.Reflection", "BindingFlags"))) - => IntrinsicId.Type_GetMember, - - // System.Type.GetMembers (BindingFlags) - "GetMembers" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") - && calledMethod.Parameters.Count == 1 - && calledMethod.HasThis - => IntrinsicId.Type_GetMembers, - - // System.Type.GetInterface (string) - // System.Type.GetInterface (string, bool) - "GetInterface" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System", "String") - && calledMethod.HasThis - && (calledMethod.Parameters.Count == 1 || - (calledMethod.Parameters.Count == 2 && calledMethod.HasParameterOfType (1, "System", "Boolean"))) - => IntrinsicId.Type_GetInterface, - - // System.Type.AssemblyQualifiedName - "get_AssemblyQualifiedName" when calledMethod.IsDeclaredOnType ("System", "Type") - && !calledMethod.HasParameters - && calledMethod.HasThis - => IntrinsicId.Type_get_AssemblyQualifiedName, - - // System.Type.UnderlyingSystemType - "get_UnderlyingSystemType" when calledMethod.IsDeclaredOnType ("System", "Type") - && !calledMethod.HasParameters - && calledMethod.HasThis - => IntrinsicId.Type_get_UnderlyingSystemType, - - // System.Type.BaseType - "get_BaseType" when calledMethod.IsDeclaredOnType ("System", "Type") - && !calledMethod.HasParameters - && calledMethod.HasThis - => IntrinsicId.Type_get_BaseType, - - // System.Type.GetProperty (string) - // System.Type.GetProperty (string, BindingFlags) - // System.Type.GetProperty (string, Type) - // System.Type.GetProperty (string, Type[]) - // System.Type.GetProperty (string, Type, Type[]) - // System.Type.GetProperty (string, Type, Type[], ParameterModifier[]) - // System.Type.GetProperty (string, BindingFlags, Binder, Type, Type[], ParameterModifier[]) - "GetProperty" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System", "String") - && calledMethod.HasThis - => IntrinsicId.Type_GetProperty, - - // System.Type.GetProperties (BindingFlags) - "GetProperties" when calledMethod.IsDeclaredOnType ("System", "Type") - && calledMethod.HasParameterOfType (0, "System.Reflection", "BindingFlags") - && calledMethod.Parameters.Count == 1 - && calledMethod.HasThis - => IntrinsicId.Type_GetProperties, - - // static System.Object.GetType () - "GetType" when calledMethod.IsDeclaredOnType ("System", "Object") - => IntrinsicId.Object_GetType, - - ".ctor" when calledMethod.IsDeclaredOnType ("System.Reflection", "TypeDelegator") - && calledMethod.HasParameterOfType (0, "System", "Type") - => IntrinsicId.TypeDelegator_Ctor, - - "Empty" when calledMethod.IsDeclaredOnType ("System", "Array") - => IntrinsicId.Array_Empty, - - // static System.Activator.CreateInstance (System.Type type) - // static System.Activator.CreateInstance (System.Type type, bool nonPublic) - // static System.Activator.CreateInstance (System.Type type, params object?[]? args) - // static System.Activator.CreateInstance (System.Type type, object?[]? args, object?[]? activationAttributes) - // static System.Activator.CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture) - // static System.Activator.CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; } - "CreateInstance" when calledMethod.IsDeclaredOnType ("System", "Activator") - && !calledMethod.ContainsGenericParameter - && calledMethod.HasParameterOfType (0, "System", "Type") - => IntrinsicId.Activator_CreateInstance_Type, - - // static System.Activator.CreateInstance (string assemblyName, string typeName) - // static System.Activator.CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) - // static System.Activator.CreateInstance (string assemblyName, string typeName, object?[]? activationAttributes) - "CreateInstance" when calledMethod.IsDeclaredOnType ("System", "Activator") - && !calledMethod.ContainsGenericParameter - && calledMethod.HasParameterOfType (0, "System", "String") - && calledMethod.HasParameterOfType (1, "System", "String") - => IntrinsicId.Activator_CreateInstance_AssemblyName_TypeName, - - // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName) - // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) - "CreateInstanceFrom" when calledMethod.IsDeclaredOnType ("System", "Activator") - && !calledMethod.ContainsGenericParameter - && calledMethod.HasParameterOfType (0, "System", "String") - && calledMethod.HasParameterOfType (1, "System", "String") - => IntrinsicId.Activator_CreateInstanceFrom, - - // static T System.Activator.CreateInstance () - "CreateInstance" when calledMethod.IsDeclaredOnType ("System", "Activator") - && calledMethod.ContainsGenericParameter - && calledMethod.GenericParameters.Count == 1 - && calledMethod.Parameters.Count == 0 - => IntrinsicId.Activator_CreateInstanceOfT, - - // System.AppDomain.CreateInstance (string assemblyName, string typeName) - // System.AppDomain.CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // System.AppDomain.CreateInstance (string assemblyName, string typeName, object? []? activationAttributes) - "CreateInstance" when calledMethod.IsDeclaredOnType ("System", "AppDomain") - && calledMethod.HasParameterOfType (0, "System", "String") - && calledMethod.HasParameterOfType (1, "System", "String") - => IntrinsicId.AppDomain_CreateInstance, - - // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName) - // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName, object? []? activationAttributes) - "CreateInstanceAndUnwrap" when calledMethod.IsDeclaredOnType ("System", "AppDomain") - && calledMethod.HasParameterOfType (0, "System", "String") - && calledMethod.HasParameterOfType (1, "System", "String") - => IntrinsicId.AppDomain_CreateInstanceAndUnwrap, - - // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName) - // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) - "CreateInstanceFrom" when calledMethod.IsDeclaredOnType ("System", "AppDomain") - && calledMethod.HasParameterOfType (0, "System", "String") - && calledMethod.HasParameterOfType (1, "System", "String") - => IntrinsicId.AppDomain_CreateInstanceFrom, - - // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName) - // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, object? []? activationAttributes) - "CreateInstanceFromAndUnwrap" when calledMethod.IsDeclaredOnType ("System", "AppDomain") - && calledMethod.HasParameterOfType (0, "System", "String") - && calledMethod.HasParameterOfType (1, "System", "String") - => IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap, - - // System.Reflection.Assembly.CreateInstance (string typeName) - // System.Reflection.Assembly.CreateInstance (string typeName, bool ignoreCase) - // System.Reflection.Assembly.CreateInstance (string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder? binder, object []? args, CultureInfo? culture, object []? activationAttributes) - "CreateInstance" when calledMethod.IsDeclaredOnType ("System.Reflection", "Assembly") - && calledMethod.HasParameterOfType (0, "System", "String") - => IntrinsicId.Assembly_CreateInstance, - - // System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) - "RunClassConstructor" when calledMethod.IsDeclaredOnType ("System.Runtime.CompilerServices", "RuntimeHelpers") - && calledMethod.HasParameterOfType (0, "System", "RuntimeTypeHandle") - => IntrinsicId.RuntimeHelpers_RunClassConstructor, - - // System.Reflection.MethodInfo.MakeGenericMethod (Type[] typeArguments) - "MakeGenericMethod" when calledMethod.IsDeclaredOnType ("System.Reflection", "MethodInfo") - && calledMethod.HasThis - && calledMethod.Parameters.Count == 1 - => IntrinsicId.MethodInfo_MakeGenericMethod, - - _ => IntrinsicId.None, - }; - } - - public override bool HandleCall (MethodBody callingMethodBody, MethodReference calledMethod, Instruction operation, ValueNodeList methodParams, out ValueNode? methodReturnValue) - { - methodReturnValue = null; + methodReturnValue = new (); + MultiValue? maybeMethodReturnValue = null; var reflectionProcessed = _markStep.ProcessReflectionDependency (callingMethodBody, operation); if (reflectionProcessed) @@ -640,1086 +241,69 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c if (calledMethodDefinition == null) return false; - _scopeStack.UpdateCurrentScopeInstructionOffset (operation.Offset); - var reflectionContext = new ReflectionPatternContext ( - _context, - ShouldEnableReflectionPatternReporting (), - _scopeStack.CurrentScope.Origin, - calledMethodDefinition, - operation); - - DynamicallyAccessedMemberTypes returnValueDynamicallyAccessedMemberTypes = 0; - - try { - - bool requiresDataFlowAnalysis = _context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (calledMethodDefinition); - returnValueDynamicallyAccessedMemberTypes = requiresDataFlowAnalysis ? - _context.Annotations.FlowAnnotations.GetReturnParameterAnnotation (calledMethodDefinition) : 0; - - switch (GetIntrinsicIdForMethod (calledMethodDefinition)) { - case IntrinsicId.IntrospectionExtensions_GetTypeInfo: { - // typeof(Foo).GetTypeInfo()... will be commonly present in code targeting - // the dead-end reflection refactoring. The call doesn't do anything and we - // don't want to lose the annotation. - methodReturnValue = methodParams[0]; - } - break; - - case IntrinsicId.TypeInfo_AsType: { - // someType.AsType()... will be commonly present in code targeting - // the dead-end reflection refactoring. The call doesn't do anything and we - // don't want to lose the annotation. - methodReturnValue = methodParams[0]; - } - break; - - case IntrinsicId.TypeDelegator_Ctor: { - // This is an identity function for analysis purposes - if (operation.OpCode == OpCodes.Newobj) - methodReturnValue = methodParams[1]; - } - break; - - case IntrinsicId.Array_Empty: { - methodReturnValue = new ArrayValue (new ConstIntValue (0), ((GenericInstanceMethod) calledMethod).GenericArguments[0]); - } - break; - - case IntrinsicId.Type_GetTypeFromHandle: { - // Infrastructure piece to support "typeof(Foo)" - if (methodParams[0] is RuntimeTypeHandleValue typeHandle) - methodReturnValue = new SystemTypeValue (typeHandle.TypeRepresented); - else if (methodParams[0] is RuntimeTypeHandleForGenericParameterValue typeHandleForGenericParameter) { - methodReturnValue = new SystemTypeForGenericParameterValue ( - typeHandleForGenericParameter.GenericParameter, - _context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (typeHandleForGenericParameter.GenericParameter)); - } - } - break; - - case IntrinsicId.Type_get_TypeHandle: { - foreach (var value in methodParams[0].UniqueValues ()) { - if (value is SystemTypeValue typeValue) - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new RuntimeTypeHandleValue (typeValue.TypeRepresented)); - else if (value == NullValue.Instance) - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, value); - else - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, UnknownValue.Instance); - } - } - break; - - // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle) - // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle, RuntimeTypeHandle declaringType) - case IntrinsicId.MethodBase_GetMethodFromHandle: { - // Infrastructure piece to support "ldtoken method -> GetMethodFromHandle" - if (methodParams[0] is RuntimeMethodHandleValue methodHandle) - methodReturnValue = new SystemReflectionMethodBaseValue (methodHandle.MethodRepresented); - } - break; - - // - // System.Type - // - // Type MakeGenericType (params Type[] typeArguments) - // - case IntrinsicId.Type_MakeGenericType: { - reflectionContext.AnalyzingPattern (); - foreach (var value in methodParams[0].UniqueValues ()) { - if (value is SystemTypeValue typeValue) { - if (AnalyzeGenericInstantiationTypeArray (methodParams[1], ref reflectionContext, calledMethodDefinition, typeValue.TypeRepresented.GenericParameters)) { - reflectionContext.RecordHandledPattern (); - } else { - bool hasUncheckedAnnotation = false; - foreach (var genericParameter in typeValue.TypeRepresented.GenericParameters) { - if (_context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (genericParameter) != DynamicallyAccessedMemberTypes.None || - (genericParameter.HasDefaultConstructorConstraint && !typeValue.TypeRepresented.IsTypeOf ("System", "Nullable`1"))) { - // If we failed to analyze the array, we go through the analyses again - // and intentionally ignore one particular annotation: - // Special case: Nullable where T : struct - // The struct constraint in C# implies new() constraints, but Nullable doesn't make a use of that part. - // There are several places even in the framework where typeof(Nullable<>).MakeGenericType would warn - // without any good reason to do so. - hasUncheckedAnnotation = true; - break; - } - } - if (hasUncheckedAnnotation) { - reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericType, - new DiagnosticString (DiagnosticId.MakeGenericType).GetMessage (calledMethodDefinition.GetDisplayName ())); - } - } - - // We haven't found any generic parameters with annotations, so there's nothing to validate. - reflectionContext.RecordHandledPattern (); - } else if (value == NullValue.Instance) - reflectionContext.RecordHandledPattern (); - else { - // We have no way to "include more" to fix this if we don't know, so we have to warn - reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericType, - new DiagnosticString (DiagnosticId.MakeGenericType).GetMessage (calledMethodDefinition.GetDisplayName ())); - } - } - - // We don't want to lose track of the type - // in case this is e.g. Activator.CreateInstance(typeof(Foo<>).MakeGenericType(...)); - methodReturnValue = methodParams[0]; - } - break; - - // - // System.Reflection.RuntimeReflectionExtensions - // - // static GetRuntimeEvent (this Type type, string name) - // static GetRuntimeField (this Type type, string name) - // static GetRuntimeMethod (this Type type, string name, Type[] parameters) - // static GetRuntimeProperty (this Type type, string name) - // - case var getRuntimeMember when getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent - || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField - || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod - || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: { - - reflectionContext.AnalyzingPattern (); - BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; - DynamicallyAccessedMemberTypes requiredMemberTypes = getRuntimeMember switch { - IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent => DynamicallyAccessedMemberTypes.PublicEvents, - IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField => DynamicallyAccessedMemberTypes.PublicFields, - IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod => DynamicallyAccessedMemberTypes.PublicMethods, - IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty => DynamicallyAccessedMemberTypes.PublicProperties, - _ => throw new InternalErrorException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is of unexpected member type."), - }; - - foreach (var value in methodParams[0].UniqueValues ()) { - if (value is SystemTypeValue systemTypeValue) { - foreach (var stringParam in methodParams[1].UniqueValues ()) { - if (stringParam is KnownStringValue stringValue) { - switch (getRuntimeMember) { - case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent: - MarkEventsOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, e => e.Name == stringValue.Contents, bindingFlags); - reflectionContext.RecordHandledPattern (); - break; - case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField: - MarkFieldsOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, f => f.Name == stringValue.Contents, bindingFlags); - reflectionContext.RecordHandledPattern (); - break; - case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod: - ProcessGetMethodByName (ref reflectionContext, systemTypeValue.TypeRepresented, stringValue.Contents, bindingFlags, ref methodReturnValue); - reflectionContext.RecordHandledPattern (); - break; - case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: - MarkPropertiesOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, p => p.Name == stringValue.Contents, bindingFlags); - reflectionContext.RecordHandledPattern (); - break; - default: - throw new InternalErrorException ($"Error processing reflection call '{calledMethod.GetDisplayName ()}' inside {callingMethodDefinition.GetDisplayName ()}. Unexpected member kind."); - } - } else { - RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethod.Parameters[0]); - } - } - } else { - RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethod.Parameters[0]); - } - } - } - break; - - // - // System.Linq.Expressions.Expression - // - // static Call (Type, String, Type[], Expression[]) - // - case IntrinsicId.Expression_Call: { - reflectionContext.AnalyzingPattern (); - BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; - - bool hasTypeArguments = (methodParams[2] as ArrayValue)?.Size.AsConstInt () != 0; - foreach (var value in methodParams[0].UniqueValues ()) { - if (value is SystemTypeValue systemTypeValue) { - foreach (var stringParam in methodParams[1].UniqueValues ()) { - if (stringParam is KnownStringValue stringValue) { - foreach (var method in systemTypeValue.TypeRepresented.GetMethodsOnTypeHierarchy (_context, m => m.Name == stringValue.Contents, bindingFlags)) { - ValidateGenericMethodInstantiation (ref reflectionContext, method, methodParams[2], calledMethod); - MarkMethod (ref reflectionContext, method); - } - - reflectionContext.RecordHandledPattern (); - } else { - if (hasTypeArguments) { - // We don't know what method the `MakeGenericMethod` was called on, so we have to assume - // that the method may have requirements which we can't fullfil -> warn. - reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericMethod, - new DiagnosticString (DiagnosticId.MakeGenericMethod).GetMessage (DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod))); - } - - RequireDynamicallyAccessedMembers ( - ref reflectionContext, - GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags), - value, - calledMethod.Parameters[0]); - } - } - } else { - if (hasTypeArguments) { - // We don't know what method the `MakeGenericMethod` was called on, so we have to assume - // that the method may have requirements which we can't fullfil -> warn. - reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericMethod, - new DiagnosticString (DiagnosticId.MakeGenericMethod).GetMessage (DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod))); - } - - RequireDynamicallyAccessedMembers ( - ref reflectionContext, - GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags), - value, - calledMethod.Parameters[0]); - } - } - } - break; - - // - // System.Linq.Expressions.Expression - // - // static Property (Expression, MethodInfo) - // - case IntrinsicId.Expression_Property when calledMethod.HasParameterOfType (1, "System.Reflection", "MethodInfo"): { - reflectionContext.AnalyzingPattern (); - - foreach (var value in methodParams[1].UniqueValues ()) { - if (value is SystemReflectionMethodBaseValue methodBaseValue) { - // We have one of the accessors for the property. The Expression.Property will in this case search - // for the matching PropertyInfo and store that. So to be perfectly correct we need to mark the - // respective PropertyInfo as "accessed via reflection". - if (methodBaseValue.MethodRepresented.TryGetProperty (out PropertyDefinition? propertyDefinition)) { - MarkProperty (ref reflectionContext, propertyDefinition); - continue; - } - } else if (value == NullValue.Instance) { - reflectionContext.RecordHandledPattern (); - continue; - } - - // In all other cases we may not even know which type this is about, so there's nothing we can do - // report it as a warning. - reflectionContext.RecordUnrecognizedPattern ( - 2103, string.Format (Resources.Strings.IL2103, - DiagnosticUtilities.GetParameterNameForErrorMessage (calledMethod.Parameters[1]), - DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod))); - } - } - break; - - // - // System.Linq.Expressions.Expression - // - // static Field (Expression, Type, String) - // static Property (Expression, Type, String) - // - case var fieldOrPropertyInstrinsic when fieldOrPropertyInstrinsic == IntrinsicId.Expression_Field || fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property: { - reflectionContext.AnalyzingPattern (); - DynamicallyAccessedMemberTypes memberTypes = fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property - ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties - : DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields; - - foreach (var value in methodParams[1].UniqueValues ()) { - if (value is SystemTypeValue systemTypeValue) { - foreach (var stringParam in methodParams[2].UniqueValues ()) { - if (stringParam is KnownStringValue stringValue) { - BindingFlags bindingFlags = methodParams[0]?.Kind == ValueNodeKind.Null ? BindingFlags.Static : BindingFlags.Default; - if (fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property) { - MarkPropertiesOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, filter: p => p.Name == stringValue.Contents, bindingFlags); - } else { - MarkFieldsOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, filter: f => f.Name == stringValue.Contents, bindingFlags); - } - - reflectionContext.RecordHandledPattern (); - } else { - RequireDynamicallyAccessedMembers (ref reflectionContext, memberTypes, value, calledMethod.Parameters[2]); - } - } - } else { - RequireDynamicallyAccessedMembers (ref reflectionContext, memberTypes, value, calledMethod.Parameters[1]); - } - } - } - break; - - // - // System.Linq.Expressions.Expression - // - // static New (Type) - // - case IntrinsicId.Expression_New: { - reflectionContext.AnalyzingPattern (); - - foreach (var value in methodParams[0].UniqueValues ()) { - if (value is SystemTypeValue systemTypeValue) { - MarkConstructorsOnType (ref reflectionContext, systemTypeValue.TypeRepresented, null, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - reflectionContext.RecordHandledPattern (); - } else { - RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, value, calledMethod.Parameters[0]); - } - } - } - break; - - // - // System.Object - // - // GetType() - // - case IntrinsicId.Object_GetType: { - foreach (var valueNode in methodParams[0].UniqueValues ()) { - // Note that valueNode can be statically typed in IL as some generic argument type. - // For example: - // void Method(T instance) { instance.GetType().... } - // Currently this case will end up with null StaticType - since there's no typedef for the generic argument type. - // But it could be that T is annotated with for example PublicMethods: - // void Method<[DAM(PublicMethods)] T>(T instance) { instance.GetType().GetMethod("Test"); } - // In this case it's in theory possible to handle it, by treating the T basically as a base class - // for the actual type of "instance". But the analysis for this would be pretty complicated (as the marking - // has to happen on the callsite, which doesn't know that GetType() will be used...). - // For now we're intentionally ignoring this case - it will produce a warning. - // The counter example is: - // Method(new Derived); - // In this case to get correct results, trimmer would have to mark all public methods on Derived. Which - // currently it won't do. - - TypeDefinition? staticType = valueNode.StaticType; - if (staticType is null) { - // We don't know anything about the type GetType was called on. Track this as a usual result of a method call without any annotations - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod)); - } else if (staticType.IsSealed || staticType.IsTypeOf ("System", "Delegate")) { - // We can treat this one the same as if it was a typeof() expression - - // We can allow Object.GetType to be modeled as System.Delegate because we keep all methods - // on delegates anyway so reflection on something this approximation would miss is actually safe. - - // We ignore the fact that the type can be annotated (see below for handling of annotated types) - // This means the annotations (if any) won't be applied - instead we rely on the exact knowledge - // of the type. So for example even if the type is annotated with PublicMethods - // but the code calls GetProperties on it - it will work - mark properties, don't mark methods - // since we ignored the fact that it's annotated. - // This can be seen a little bit as a violation of the annotation, but we already have similar cases - // where a parameter is annotated and if something in the method sets a specific known type to it - // we will also make it just work, even if the annotation doesn't match the usage. - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (staticType)); - } else { - reflectionContext.AnalyzingPattern (); - - // Make sure the type is marked (this will mark it as used via reflection, which is sort of true) - // This should already be true for most cases (method params, fields, ...), but just in case - MarkType (ref reflectionContext, staticType); - - var annotation = _markStep.DynamicallyAccessedMembersTypeHierarchy - .ApplyDynamicallyAccessedMembersToTypeHierarchy (this, staticType); - - reflectionContext.RecordHandledPattern (); - - // Return a value which is "unknown type" with annotation. For now we'll use the return value node - // for the method, which means we're loosing the information about which staticType this - // started with. For now we don't need it, but we can add it later on. - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod, annotation)); - } - } - } - break; - - // - // System.Type - // - // GetType (string) - // GetType (string, Boolean) - // GetType (string, Boolean, Boolean) - // GetType (string, Func, Func) - // GetType (string, Func, Func, Boolean) - // GetType (string, Func, Func, Boolean, Boolean) - // - case IntrinsicId.Type_GetType: { - reflectionContext.AnalyzingPattern (); - - var parameters = calledMethod.Parameters; - if ((parameters.Count == 3 && parameters[2].ParameterType.MetadataType == MetadataType.Boolean && methodParams[2].AsConstInt () != 0) || - (parameters.Count == 5 && methodParams[4].AsConstInt () != 0)) { - reflectionContext.RecordUnrecognizedPattern (2096, $"Call to '{calledMethod.GetDisplayName ()}' can perform case insensitive lookup of the type, currently ILLink can not guarantee presence of all the matching types"); - break; - } - foreach (var typeNameValue in methodParams[0].UniqueValues ()) { - if (typeNameValue is KnownStringValue knownStringValue) { - if (!_context.TypeNameResolver.TryResolveTypeName (knownStringValue.Contents, callingMethodDefinition, out TypeReference? foundTypeRef, out AssemblyDefinition? typeAssembly, false) - || ResolveToTypeDefinition (foundTypeRef) is not TypeDefinition foundType) { - // Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. - reflectionContext.RecordHandledPattern (); - } else { - reflectionContext.RecordRecognizedPattern (foundType, () => _markStep.MarkTypeVisibleToReflection (foundTypeRef, foundType, new DependencyInfo (DependencyKind.AccessedViaReflection, callingMethodDefinition))); - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (foundType)); - _context.MarkingHelpers.MarkMatchingExportedType (foundType, typeAssembly, new DependencyInfo (DependencyKind.AccessedViaReflection, foundType), reflectionContext.Origin); - } - } else if (typeNameValue == NullValue.Instance) { - reflectionContext.RecordHandledPattern (); - } else if (typeNameValue is LeafValueWithDynamicallyAccessedMemberNode valueWithDynamicallyAccessedMember && valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes != 0) { - // Propagate the annotation from the type name to the return value. Annotation on a string value will be fullfilled whenever a value is assigned to the string with annotation. - // So while we don't know which type it is, we can guarantee that it will fulfill the annotation. - reflectionContext.RecordHandledPattern (); - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethodDefinition, valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes)); - } else { - reflectionContext.RecordUnrecognizedPattern (2057, $"Unrecognized value passed to the parameter 'typeName' of method '{calledMethod.GetDisplayName ()}'. It's not possible to guarantee the availability of the target type."); - } - } - - } - break; - - // - // GetConstructor (Type[]) - // GetConstructor (BindingFlags, Type[]) - // GetConstructor (BindingFlags, Binder, Type[], ParameterModifier []) - // GetConstructor (BindingFlags, Binder, CallingConventions, Type[], ParameterModifier []) - // - case IntrinsicId.Type_GetConstructor: { - reflectionContext.AnalyzingPattern (); - - var parameters = calledMethod.Parameters; - BindingFlags? bindingFlags; - if (parameters.Count > 1 && calledMethod.Parameters[0].ParameterType.Name == "BindingFlags") - bindingFlags = GetBindingFlagsFromValue (methodParams[1]); - else - // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter - bindingFlags = BindingFlags.Public | BindingFlags.Instance; - - int? ctorParameterCount = parameters.Count switch { - 1 => (methodParams[1] as ArrayValue)?.Size.AsConstInt (), - 2 => (methodParams[2] as ArrayValue)?.Size.AsConstInt (), - 4 => (methodParams[3] as ArrayValue)?.Size.AsConstInt (), - 5 => (methodParams[4] as ArrayValue)?.Size.AsConstInt (), - _ => null, - }; - - // Go over all types we've seen - foreach (var value in methodParams[0].UniqueValues ()) { - if (value is SystemTypeValue systemTypeValue) { - if (BindingFlagsAreUnsupported (bindingFlags)) { - RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors, value, calledMethodDefinition); - } else { - if (HasBindingFlag (bindingFlags, BindingFlags.Public) && !HasBindingFlag (bindingFlags, BindingFlags.NonPublic) - && ctorParameterCount == 0) { - MarkConstructorsOnType (ref reflectionContext, systemTypeValue.TypeRepresented, m => m.IsPublic && m.Parameters.Count == 0, bindingFlags); - } else { - MarkConstructorsOnType (ref reflectionContext, systemTypeValue.TypeRepresented, null, bindingFlags); - } - } - reflectionContext.RecordHandledPattern (); - } else { - // Otherwise fall back to the bitfield requirements - var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags); - // We can scope down the public constructors requirement if we know the number of parameters is 0 - if (requiredMemberTypes == DynamicallyAccessedMemberTypes.PublicConstructors && ctorParameterCount == 0) - requiredMemberTypes = DynamicallyAccessedMemberTypes.PublicParameterlessConstructor; - RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethodDefinition); - } - } - } - break; - - // - // GetMethod (string) - // GetMethod (string, BindingFlags) - // GetMethod (string, Type[]) - // GetMethod (string, Type[], ParameterModifier[]) - // GetMethod (string, BindingFlags, Type[]) - // GetMethod (string, BindingFlags, Binder, Type[], ParameterModifier[]) - // GetMethod (string, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[]) - // GetMethod (string, int, Type[]) - // GetMethod (string, int, Type[], ParameterModifier[]?) - // GetMethod (string, int, BindingFlags, Binder?, Type[], ParameterModifier[]?) - // GetMethod (string, int, BindingFlags, Binder?, CallingConventions, Type[], ParameterModifier[]?) - // - case IntrinsicId.Type_GetMethod: { - reflectionContext.AnalyzingPattern (); - - BindingFlags? bindingFlags; - if (calledMethod.Parameters.Count > 1 && calledMethod.Parameters[1].ParameterType.Name == "BindingFlags") - bindingFlags = GetBindingFlagsFromValue (methodParams[2]); - else if (calledMethod.Parameters.Count > 2 && calledMethod.Parameters[2].ParameterType.Name == "BindingFlags") - bindingFlags = GetBindingFlagsFromValue (methodParams[3]); - else - // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter - bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; - var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags); - foreach (var value in methodParams[0].UniqueValues ()) { - if (value is SystemTypeValue systemTypeValue) { - foreach (var stringParam in methodParams[1].UniqueValues ()) { - if (stringParam is KnownStringValue stringValue) { - if (BindingFlagsAreUnsupported (bindingFlags)) { - RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods, value, calledMethodDefinition); - } else { - ProcessGetMethodByName (ref reflectionContext, systemTypeValue.TypeRepresented, stringValue.Contents, bindingFlags, ref methodReturnValue); - } - - reflectionContext.RecordHandledPattern (); - } else { - // Otherwise fall back to the bitfield requirements - RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethodDefinition); - } - } - } else { - // Otherwise fall back to the bitfield requirements - RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethodDefinition); - } - } - } - break; - - // - // GetNestedType (string) - // GetNestedType (string, BindingFlags) - // - case IntrinsicId.Type_GetNestedType: { - reflectionContext.AnalyzingPattern (); - - BindingFlags? bindingFlags; - if (calledMethod.Parameters.Count > 1 && calledMethod.Parameters[1].ParameterType.Name == "BindingFlags") - bindingFlags = GetBindingFlagsFromValue (methodParams[2]); - else - // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter - bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; - - var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (bindingFlags); - bool everyParentTypeHasAll = true; - foreach (var value in methodParams[0].UniqueValues ()) { - if (value is SystemTypeValue systemTypeValue) { - foreach (var stringParam in methodParams[1].UniqueValues ()) { - if (stringParam is KnownStringValue stringValue) { - if (BindingFlagsAreUnsupported (bindingFlags)) - // We have chosen not to populate the methodReturnValue for now - RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, value, calledMethodDefinition); - else { - TypeDefinition[]? matchingNestedTypes = MarkNestedTypesOnType (ref reflectionContext, systemTypeValue.TypeRepresented, m => m.Name == stringValue.Contents, bindingFlags); - - if (matchingNestedTypes != null) { - for (int i = 0; i < matchingNestedTypes.Length; i++) - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (matchingNestedTypes[i])); - } - } - reflectionContext.RecordHandledPattern (); - } else { - // Otherwise fall back to the bitfield requirements - RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethodDefinition); - } - } - } else { - // Otherwise fall back to the bitfield requirements - RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethodDefinition); - } - - if (value is LeafValueWithDynamicallyAccessedMemberNode leafValueWithDynamicallyAccessedMember) { - if (leafValueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.All) - everyParentTypeHasAll = false; - } else if (!(value is NullValue || value is SystemTypeValue)) { - // Known Type values are always OK - either they're fully resolved above and thus the return value - // is set to the known resolved type, or if they're not resolved, they won't exist at runtime - // and will cause exceptions - and thus don't introduce new requirements on marking. - // nulls are intentionally ignored as they will lead to exceptions at runtime - // and thus don't introduce new requirements on marking. - everyParentTypeHasAll = false; - } - } - - // If the parent type (all the possible values) has DynamicallyAccessedMemberTypes.All it means its nested types are also fully marked - // (see MarkStep.MarkEntireType - it will recursively mark entire type on nested types). In that case we can annotate - // the returned type (the nested type) with DynamicallyAccessedMemberTypes.All as well. - // Note it's OK to blindly overwrite any potential annotation on the return value from the method definition - // since DynamicallyAccessedMemberTypes.All is a superset of any other annotation. - if (everyParentTypeHasAll && methodReturnValue == null) - methodReturnValue = CreateMethodReturnValue (calledMethodDefinition, DynamicallyAccessedMemberTypes.All); - } - break; - - // - // AssemblyQualifiedName - // - case IntrinsicId.Type_get_AssemblyQualifiedName: { - ValueNode? transformedResult = null; - foreach (var value in methodParams[0].UniqueValues ()) { - if (value is LeafValueWithDynamicallyAccessedMemberNode dynamicallyAccessedThing) { - var annotatedString = new AnnotatedStringValue (dynamicallyAccessedThing.SourceContext, dynamicallyAccessedThing.DynamicallyAccessedMemberTypes); - transformedResult = MergePointValue.MergeValues (transformedResult, annotatedString); - } else { - transformedResult = null; - break; - } - } - - if (transformedResult != null) { - methodReturnValue = transformedResult; - } - } - break; - - // - // UnderlyingSystemType - // - case IntrinsicId.Type_get_UnderlyingSystemType: { - // This is identity for the purposes of the analysis. - methodReturnValue = methodParams[0]; - } - break; - - // - // Type.BaseType - // - case IntrinsicId.Type_get_BaseType: { - foreach (var value in methodParams[0].UniqueValues ()) { - if (value is LeafValueWithDynamicallyAccessedMemberNode dynamicallyAccessedMemberNode) { - DynamicallyAccessedMemberTypes propagatedMemberTypes = DynamicallyAccessedMemberTypes.None; - if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All) - propagatedMemberTypes = DynamicallyAccessedMemberTypes.All; - else { - // PublicConstructors are not propagated to base type - - if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicEvents)) - propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicEvents; - - if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicFields)) - propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicFields; - - if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicMethods)) - propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicMethods; - - // PublicNestedTypes are not propagated to base type - - // PublicParameterlessConstructor is not propagated to base type - - if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicProperties)) - propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicProperties; - } - - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod, propagatedMemberTypes)); - } else if (value is SystemTypeValue systemTypeValue) { - if (systemTypeValue.TypeRepresented.BaseType is TypeReference baseTypeRef && _context.TryResolve (baseTypeRef) is TypeDefinition baseTypeDefinition) - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (baseTypeDefinition)); - else - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod)); - } else if (value == NullValue.Instance) { - // Ignore nulls - null.BaseType will fail at runtime, but it has no effect on static analysis - continue; - } else { - // Unknown input - propagate a return value without any annotation - we know it's a Type but we know nothing about it - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod)); - } - } - } - break; - - // - // GetField (string) - // GetField (string, BindingFlags) - // GetEvent (string) - // GetEvent (string, BindingFlags) - // GetProperty (string) - // GetProperty (string, BindingFlags) - // GetProperty (string, Type) - // GetProperty (string, Type[]) - // GetProperty (string, Type, Type[]) - // GetProperty (string, Type, Type[], ParameterModifier[]) - // GetProperty (string, BindingFlags, Binder, Type, Type[], ParameterModifier[]) - // - case var fieldPropertyOrEvent when (fieldPropertyOrEvent == IntrinsicId.Type_GetField || fieldPropertyOrEvent == IntrinsicId.Type_GetProperty || fieldPropertyOrEvent == IntrinsicId.Type_GetEvent) - && calledMethod.DeclaringType.Namespace == "System" - && calledMethod.DeclaringType.Name == "Type" - && calledMethod.Parameters[0].ParameterType.FullName == "System.String" - && calledMethod.HasThis: { - - reflectionContext.AnalyzingPattern (); - BindingFlags? bindingFlags; - if (calledMethod.Parameters.Count > 1 && calledMethod.Parameters[1].ParameterType.Name == "BindingFlags") - bindingFlags = GetBindingFlagsFromValue (methodParams[2]); - else - // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter - bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; - - DynamicallyAccessedMemberTypes memberTypes = fieldPropertyOrEvent switch { - IntrinsicId.Type_GetEvent => GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents (bindingFlags), - IntrinsicId.Type_GetField => GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields (bindingFlags), - IntrinsicId.Type_GetProperty => GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties (bindingFlags), - _ => throw new ArgumentException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is of unexpected member type."), - }; - - foreach (var value in methodParams[0].UniqueValues ()) { - if (value is SystemTypeValue systemTypeValue) { - foreach (var stringParam in methodParams[1].UniqueValues ()) { - if (stringParam is KnownStringValue stringValue) { - switch (fieldPropertyOrEvent) { - case IntrinsicId.Type_GetEvent: - if (BindingFlagsAreUnsupported (bindingFlags)) - RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents, value, calledMethodDefinition); - else - MarkEventsOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, filter: e => e.Name == stringValue.Contents, bindingFlags); - break; - case IntrinsicId.Type_GetField: - if (BindingFlagsAreUnsupported (bindingFlags)) - RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields, value, calledMethodDefinition); - else - MarkFieldsOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, filter: f => f.Name == stringValue.Contents, bindingFlags); - break; - case IntrinsicId.Type_GetProperty: - if (BindingFlagsAreUnsupported (bindingFlags)) - RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties, value, calledMethodDefinition); - else - MarkPropertiesOnTypeHierarchy (ref reflectionContext, systemTypeValue.TypeRepresented, filter: p => p.Name == stringValue.Contents, bindingFlags); - break; - default: - Debug.Fail ("Unreachable."); - break; - } - reflectionContext.RecordHandledPattern (); - } else { - RequireDynamicallyAccessedMembers (ref reflectionContext, memberTypes, value, calledMethodDefinition); - } - } - } else { - RequireDynamicallyAccessedMembers (ref reflectionContext, memberTypes, value, calledMethodDefinition); - } - } - } - break; - - // - // GetConstructors (BindingFlags) - // GetMethods (BindingFlags) - // GetFields (BindingFlags) - // GetEvents (BindingFlags) - // GetProperties (BindingFlags) - // GetNestedTypes (BindingFlags) - // GetMembers (BindingFlags) - // - case var callType when (callType == IntrinsicId.Type_GetConstructors || callType == IntrinsicId.Type_GetMethods || callType == IntrinsicId.Type_GetFields || - callType == IntrinsicId.Type_GetProperties || callType == IntrinsicId.Type_GetEvents || callType == IntrinsicId.Type_GetNestedTypes || callType == IntrinsicId.Type_GetMembers) - && calledMethod.DeclaringType.Namespace == "System" - && calledMethod.DeclaringType.Name == "Type" - && calledMethod.Parameters[0].ParameterType.FullName == "System.Reflection.BindingFlags" - && calledMethod.HasThis: { - - reflectionContext.AnalyzingPattern (); - BindingFlags? bindingFlags; - bindingFlags = GetBindingFlagsFromValue (methodParams[1]); - DynamicallyAccessedMemberTypes memberTypes = DynamicallyAccessedMemberTypes.None; - if (BindingFlagsAreUnsupported (bindingFlags)) { - memberTypes = callType switch { - IntrinsicId.Type_GetConstructors => DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors, - IntrinsicId.Type_GetMethods => DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods, - IntrinsicId.Type_GetEvents => DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents, - IntrinsicId.Type_GetFields => DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields, - IntrinsicId.Type_GetProperties => DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties, - IntrinsicId.Type_GetNestedTypes => DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, - IntrinsicId.Type_GetMembers => DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | - DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | - DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | - DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | - DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, - _ => throw new ArgumentException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is of unexpected member type."), - }; - } else { - memberTypes = callType switch { - IntrinsicId.Type_GetConstructors => GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags), - IntrinsicId.Type_GetMethods => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags), - IntrinsicId.Type_GetEvents => GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents (bindingFlags), - IntrinsicId.Type_GetFields => GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields (bindingFlags), - IntrinsicId.Type_GetProperties => GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties (bindingFlags), - IntrinsicId.Type_GetNestedTypes => GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (bindingFlags), - IntrinsicId.Type_GetMembers => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers (bindingFlags), - _ => throw new ArgumentException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is of unexpected member type."), - }; - } - - foreach (var value in methodParams[0].UniqueValues ()) { - RequireDynamicallyAccessedMembers (ref reflectionContext, memberTypes, value, calledMethodDefinition); - } - } - break; - - - // - // GetMember (String) - // GetMember (String, BindingFlags) - // GetMember (String, MemberTypes, BindingFlags) - // - case IntrinsicId.Type_GetMember: { - reflectionContext.AnalyzingPattern (); - var parameters = calledMethod.Parameters; - BindingFlags? bindingFlags; - if (parameters.Count == 1) { - // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter - bindingFlags = BindingFlags.Public | BindingFlags.Instance; - } else if (parameters.Count == 2 && calledMethod.Parameters[1].ParameterType.Name == "BindingFlags") - bindingFlags = GetBindingFlagsFromValue (methodParams[2]); - else if (parameters.Count == 3 && calledMethod.Parameters[2].ParameterType.Name == "BindingFlags") { - bindingFlags = GetBindingFlagsFromValue (methodParams[3]); - } else // Non recognized intrinsic - throw new ArgumentException ($"Reflection call '{calledMethod.GetDisplayName ()}' inside '{callingMethodDefinition.GetDisplayName ()}' is an unexpected intrinsic."); - - DynamicallyAccessedMemberTypes requiredMemberTypes = DynamicallyAccessedMemberTypes.None; - if (BindingFlagsAreUnsupported (bindingFlags)) { - requiredMemberTypes = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | - DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | - DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | - DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | - DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; - } else { - requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers (bindingFlags); - } - // Go over all types we've seen - foreach (var value in methodParams[0].UniqueValues ()) { - // Mark based on bitfield requirements - RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethodDefinition); - } - } - break; - - // - // GetInterface (String) - // GetInterface (String, bool) - // - case IntrinsicId.Type_GetInterface: { - reflectionContext.AnalyzingPattern (); - - foreach (var value in methodParams[0].UniqueValues ()) { - // For now no support for marking a single interface by name. We would have to correctly support - // mangled names for generics to do that correctly. Simply mark all interfaces on the type for now. - - // Require Interfaces annotation - RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypesOverlay.Interfaces, value, calledMethodDefinition); - - // Interfaces is transitive, so the return values will always have at least Interfaces annotation - DynamicallyAccessedMemberTypes returnMemberTypes = DynamicallyAccessedMemberTypesOverlay.Interfaces; - - // Propagate All annotation across the call - All is a superset of Interfaces - if (value is LeafValueWithDynamicallyAccessedMemberNode annotatedNode - && annotatedNode.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All) - returnMemberTypes = DynamicallyAccessedMemberTypes.All; - - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod, returnMemberTypes)); - } - } - break; - - // - // System.Activator - // - // static CreateInstance (System.Type type) - // static CreateInstance (System.Type type, bool nonPublic) - // static CreateInstance (System.Type type, params object?[]? args) - // static CreateInstance (System.Type type, object?[]? args, object?[]? activationAttributes) - // static CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture) - // static CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; } - // - case IntrinsicId.Activator_CreateInstance_Type: { - var parameters = calledMethod.Parameters; - - reflectionContext.AnalyzingPattern (); - - int? ctorParameterCount = null; - BindingFlags bindingFlags = BindingFlags.Instance; - if (parameters.Count > 1) { - if (parameters[1].ParameterType.MetadataType == MetadataType.Boolean) { - // The overload that takes a "nonPublic" bool - bool nonPublic = true; - if (methodParams[1] is ConstIntValue constInt) { - nonPublic = constInt.Value != 0; - } - - if (nonPublic) - bindingFlags |= BindingFlags.NonPublic | BindingFlags.Public; - else - bindingFlags |= BindingFlags.Public; - ctorParameterCount = 0; - } else { - // Overload that has the parameters as the second or fourth argument - int argsParam = parameters.Count == 2 || parameters.Count == 3 ? 1 : 3; - - if (methodParams.Count > argsParam) { - if (methodParams[argsParam] is ArrayValue arrayValue && - arrayValue.Size.AsConstInt () != null) - ctorParameterCount = arrayValue.Size.AsConstInt (); - else if (methodParams[argsParam] is NullValue) - ctorParameterCount = 0; - } - - if (parameters.Count > 3) { - if (methodParams[1].AsConstInt () is int constInt) - bindingFlags |= (BindingFlags) constInt; - else - bindingFlags |= BindingFlags.NonPublic | BindingFlags.Public; - } else { - bindingFlags |= BindingFlags.Public; - } - } - } else { - // The overload with a single System.Type argument - ctorParameterCount = 0; - bindingFlags |= BindingFlags.Public; - } - - // Go over all types we've seen - foreach (var value in methodParams[0].UniqueValues ()) { - if (value is SystemTypeValue systemTypeValue) { - // Special case known type values as we can do better by applying exact binding flags and parameter count. - MarkConstructorsOnType (ref reflectionContext, systemTypeValue.TypeRepresented, - ctorParameterCount == null ? null : m => m.Parameters.Count == ctorParameterCount, bindingFlags); - reflectionContext.RecordHandledPattern (); - } else { - // Otherwise fall back to the bitfield requirements - var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags); - - // Special case the public parameterless constructor if we know that there are 0 args passed in - if (ctorParameterCount == 0 && requiredMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors)) { - requiredMemberTypes &= ~DynamicallyAccessedMemberTypes.PublicConstructors; - requiredMemberTypes |= DynamicallyAccessedMemberTypes.PublicParameterlessConstructor; - } - - RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, value, calledMethod.Parameters[0]); - } - } - } - break; - - // - // System.Activator - // - // static CreateInstance (string assemblyName, string typeName) - // static CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) - // static CreateInstance (string assemblyName, string typeName, object?[]? activationAttributes) - // - case IntrinsicId.Activator_CreateInstance_AssemblyName_TypeName: - ProcessCreateInstanceByName (ref reflectionContext, calledMethodDefinition, methodParams); - break; - - // - // System.Activator - // - // static CreateInstanceFrom (string assemblyFile, string typeName) - // static CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // static CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) - // - case IntrinsicId.Activator_CreateInstanceFrom: - ProcessCreateInstanceByName (ref reflectionContext, calledMethodDefinition, methodParams); - break; - - // - // System.Activator - // - // static T CreateInstance () - // - // Note: If the when condition returns false it would be an overload which we don't recognize, so just fall through to the default case - case IntrinsicId.Activator_CreateInstanceOfT when - calledMethod is GenericInstanceMethod genericCalledMethod && genericCalledMethod.GenericArguments.Count == 1: { - reflectionContext.AnalyzingPattern (); - - if (genericCalledMethod.GenericArguments[0] is GenericParameter genericParameter && - genericParameter.HasDefaultConstructorConstraint) { - // This is safe, the linker would have marked the default .ctor already - reflectionContext.RecordHandledPattern (); - break; - } - - RequireDynamicallyAccessedMembers ( - ref reflectionContext, - DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, - GetTypeValueNodeFromGenericArgument (genericCalledMethod.GenericArguments[0]), - calledMethodDefinition.GenericParameters[0]); - } - break; - - // - // System.AppDomain - // - // CreateInstance (string assemblyName, string typeName) - // CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // CreateInstance (string assemblyName, string typeName, object? []? activationAttributes) - // - // CreateInstanceAndUnwrap (string assemblyName, string typeName) - // CreateInstanceAndUnwrap (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // CreateInstanceAndUnwrap (string assemblyName, string typeName, object? []? activationAttributes) - // - // CreateInstanceFrom (string assemblyFile, string typeName) - // CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) - // - // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName) - // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, object? []? activationAttributes) - // - case var appDomainCreateInstance when appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstance - || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceAndUnwrap - || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFrom - || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap: - ProcessCreateInstanceByName (ref reflectionContext, calledMethodDefinition, methodParams); - break; - - // - // System.Reflection.Assembly - // - // CreateInstance (string typeName) - // CreateInstance (string typeName, bool ignoreCase) - // CreateInstance (string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder? binder, object []? args, CultureInfo? culture, object []? activationAttributes) - // - case IntrinsicId.Assembly_CreateInstance: - // For now always fail since we don't track assemblies (dotnet/linker/issues/1947) - reflectionContext.AnalyzingPattern (); - reflectionContext.RecordUnrecognizedPattern (2058, $"Parameters passed to method '{calledMethodDefinition.GetDisplayName ()}' cannot be analyzed. Consider using methods 'System.Type.GetType' and `System.Activator.CreateInstance` instead."); - break; - - // - // System.Runtime.CompilerServices.RuntimeHelpers - // - // RunClassConstructor (RuntimeTypeHandle type) - // - case IntrinsicId.RuntimeHelpers_RunClassConstructor: { - reflectionContext.AnalyzingPattern (); - foreach (var typeHandleValue in methodParams[0].UniqueValues ()) { - if (typeHandleValue is RuntimeTypeHandleValue runtimeTypeHandleValue) { - _markStep.MarkStaticConstructorVisibleToReflection (runtimeTypeHandleValue.TypeRepresented, new DependencyInfo (DependencyKind.AccessedViaReflection, reflectionContext.Source)); - reflectionContext.RecordHandledPattern (); - } else if (typeHandleValue == NullValue.Instance) - reflectionContext.RecordHandledPattern (); - else { - reflectionContext.RecordUnrecognizedPattern (2059, $"Unrecognized value passed to the parameter 'type' of method '{calledMethodDefinition.GetDisplayName ()}'. It's not possible to guarantee the availability of the target static constructor."); - } - } - } - break; - - // - // System.Reflection.MethodInfo - // - // MakeGenericMethod (Type[] typeArguments) - // - case IntrinsicId.MethodInfo_MakeGenericMethod: { - reflectionContext.AnalyzingPattern (); - - foreach (var methodValue in methodParams[0].UniqueValues ()) { - if (methodValue is SystemReflectionMethodBaseValue methodBaseValue) { - ValidateGenericMethodInstantiation (ref reflectionContext, methodBaseValue.MethodRepresented, methodParams[1], calledMethod); - } else if (methodValue == NullValue.Instance) { - reflectionContext.RecordHandledPattern (); - } else { - // We don't know what method the `MakeGenericMethod` was called on, so we have to assume - // that the method may have requirements which we can't fullfil -> warn. - reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericMethod, - new DiagnosticString (DiagnosticId.MakeGenericMethod).GetMessage (DiagnosticUtilities.GetMethodSignatureDisplayName (calledMethod))); - } - } - - // MakeGenericMethod doesn't change the identity of the MethodBase we're tracking so propagate to the return value - methodReturnValue = methodParams[0]; - } - break; - - default: + bool requiresDataFlowAnalysis = _context.Annotations.FlowAnnotations.RequiresDataFlowAnalysis (calledMethodDefinition); + DynamicallyAccessedMemberTypes returnValueDynamicallyAccessedMemberTypes = requiresDataFlowAnalysis ? + _context.Annotations.FlowAnnotations.GetReturnParameterAnnotation (calledMethodDefinition) : 0; + + _origin = _origin.WithInstructionOffset (operation.Offset); + bool diagnosticsEnabled = ShouldEnableReflectionPatternReporting (_origin.Provider); + var diagnosticContext = new DiagnosticContext (_origin, diagnosticsEnabled, _context); + var handleCallAction = new HandleCallAction (_context, _reflectionMarker, diagnosticContext, callingMethodDefinition); + switch (Intrinsics.GetIntrinsicIdForMethod (calledMethodDefinition)) { + case IntrinsicId.IntrospectionExtensions_GetTypeInfo: + case IntrinsicId.TypeInfo_AsType: + case IntrinsicId.Type_get_UnderlyingSystemType: + case IntrinsicId.Type_GetTypeFromHandle: + case IntrinsicId.Type_get_TypeHandle: + case IntrinsicId.Type_GetInterface: + case IntrinsicId.Type_get_AssemblyQualifiedName: + case IntrinsicId.RuntimeHelpers_RunClassConstructor: + case var callType when (callType == IntrinsicId.Type_GetConstructors || callType == IntrinsicId.Type_GetMethods || callType == IntrinsicId.Type_GetFields || + callType == IntrinsicId.Type_GetProperties || callType == IntrinsicId.Type_GetEvents || callType == IntrinsicId.Type_GetNestedTypes || callType == IntrinsicId.Type_GetMembers) + && calledMethod.DeclaringType.IsTypeOf (WellKnownType.System_Type) + && calledMethod.Parameters[0].ParameterType.FullName == "System.Reflection.BindingFlags" + && calledMethod.HasThis: + case var fieldPropertyOrEvent when (fieldPropertyOrEvent == IntrinsicId.Type_GetField || fieldPropertyOrEvent == IntrinsicId.Type_GetProperty || fieldPropertyOrEvent == IntrinsicId.Type_GetEvent) + && calledMethod.DeclaringType.IsTypeOf (WellKnownType.System_Type) + && calledMethod.Parameters[0].ParameterType.IsTypeOf (WellKnownType.System_String) + && calledMethod.HasThis: + case var getRuntimeMember when getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: + case IntrinsicId.Type_GetMember: + case IntrinsicId.Type_GetMethod: + case IntrinsicId.Type_GetNestedType: + case IntrinsicId.Nullable_GetUnderlyingType: + case IntrinsicId.Expression_Property when calledMethod.HasParameterOfType (1, "System.Reflection.MethodInfo"): + case var fieldOrPropertyInstrinsic when fieldOrPropertyInstrinsic == IntrinsicId.Expression_Field || fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property: + case IntrinsicId.Type_get_BaseType: + case IntrinsicId.Type_GetConstructor: + case IntrinsicId.MethodBase_GetMethodFromHandle: + case IntrinsicId.MethodBase_get_MethodHandle: + case IntrinsicId.Type_MakeGenericType: + case IntrinsicId.MethodInfo_MakeGenericMethod: + case IntrinsicId.Expression_Call: + case IntrinsicId.Expression_New: + case IntrinsicId.Type_GetType: + case IntrinsicId.Activator_CreateInstance_Type: + case IntrinsicId.Activator_CreateInstance_AssemblyName_TypeName: + case IntrinsicId.Activator_CreateInstanceFrom: + case var appDomainCreateInstance when appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstance + || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceAndUnwrap + || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFrom + || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap: + case IntrinsicId.Assembly_CreateInstance: { + var instanceValue = MultiValueLattice.Top; + IReadOnlyList parameterValues = methodParams; + if (calledMethodDefinition.HasImplicitThis ()) { + instanceValue = methodParams[0]; + parameterValues = parameterValues.Skip (1).ToImmutableList (); + } + return handleCallAction.Invoke (calledMethodDefinition, instanceValue, parameterValues, out methodReturnValue, out _); + } + case IntrinsicId.None: { if (calledMethodDefinition.IsPInvokeImpl) { // Is the PInvoke dangerous? bool comDangerousMethod = IsComInterop (calledMethodDefinition.MethodReturnType, calledMethodDefinition.ReturnType); @@ -1728,62 +312,114 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c } if (comDangerousMethod) { - reflectionContext.AnalyzingPattern (); - reflectionContext.RecordUnrecognizedPattern (2050, $"P/invoke method '{calledMethodDefinition.GetDisplayName ()}' declares a parameter with COM marshalling. Correctness of COM interop cannot be guaranteed after trimming. Interfaces and interface members might be removed."); + diagnosticContext.AddDiagnostic (DiagnosticId.CorrectnessOfCOMCannotBeGuaranteed, calledMethodDefinition.GetDisplayName ()); } } + _markStep.CheckAndReportRequiresUnreferencedCode (calledMethodDefinition, _origin); - if (requiresDataFlowAnalysis) { - reflectionContext.AnalyzingPattern (); - for (int parameterIndex = 0; parameterIndex < methodParams.Count; parameterIndex++) { - var requiredMemberTypes = _context.Annotations.FlowAnnotations.GetParameterAnnotation (calledMethodDefinition, parameterIndex); - if (requiredMemberTypes != 0) { - IMetadataTokenProvider targetContext; - if (calledMethodDefinition.HasImplicitThis ()) { - if (parameterIndex == 0) - targetContext = calledMethodDefinition; - else - targetContext = calledMethodDefinition.Parameters[parameterIndex - 1]; - } else { - targetContext = calledMethodDefinition.Parameters[parameterIndex]; - } + var instanceValue = MultiValueLattice.Top; + IReadOnlyList parameterValues = methodParams; + if (calledMethodDefinition.HasImplicitThis ()) { + instanceValue = methodParams[0]; + parameterValues = parameterValues.Skip (1).ToImmutableList (); + } + return handleCallAction.Invoke (calledMethodDefinition, instanceValue, parameterValues, out methodReturnValue, out _); + } - RequireDynamicallyAccessedMembers (ref reflectionContext, requiredMemberTypes, methodParams[parameterIndex], targetContext); - } - } + case IntrinsicId.TypeDelegator_Ctor: { + // This is an identity function for analysis purposes + if (operation.OpCode == OpCodes.Newobj) + AddReturnValue (methodParams[1]); + } + break; - reflectionContext.RecordHandledPattern (); - } + case IntrinsicId.Array_Empty: { + AddReturnValue (ArrayValue.Create (0, ((GenericInstanceMethod) calledMethod).GenericArguments[0])); + } + break; - _markStep.CheckAndReportRequiresUnreferencedCode (calledMethodDefinition); + // + // System.Object + // + // GetType() + // + case IntrinsicId.Object_GetType: { + foreach (var valueNode in methodParams[0]) { + // Note that valueNode can be statically typed in IL as some generic argument type. + // For example: + // void Method(T instance) { instance.GetType().... } + // Currently this case will end up with null StaticType - since there's no typedef for the generic argument type. + // But it could be that T is annotated with for example PublicMethods: + // void Method<[DAM(PublicMethods)] T>(T instance) { instance.GetType().GetMethod("Test"); } + // In this case it's in theory possible to handle it, by treating the T basically as a base class + // for the actual type of "instance". But the analysis for this would be pretty complicated (as the marking + // has to happen on the callsite, which doesn't know that GetType() will be used...). + // For now we're intentionally ignoring this case - it will produce a warning. + // The counter example is: + // Method(new Derived); + // In this case to get correct results, trimmer would have to mark all public methods on Derived. Which + // currently it won't do. + + TypeDefinition? staticType = (valueNode as IValueWithStaticType)?.StaticType; + if (staticType is null) { + // We don't know anything about the type GetType was called on. Track this as a usual result of a method call without any annotations + AddReturnValue (_annotations.GetMethodReturnValue (calledMethodDefinition)); + } else if (staticType.IsSealed || staticType.IsTypeOf ("System", "Delegate")) { + // We can treat this one the same as if it was a typeof() expression + + // We can allow Object.GetType to be modeled as System.Delegate because we keep all methods + // on delegates anyway so reflection on something this approximation would miss is actually safe. + + // We ignore the fact that the type can be annotated (see below for handling of annotated types) + // This means the annotations (if any) won't be applied - instead we rely on the exact knowledge + // of the type. So for example even if the type is annotated with PublicMethods + // but the code calls GetProperties on it - it will work - mark properties, don't mark methods + // since we ignored the fact that it's annotated. + // This can be seen a little bit as a violation of the annotation, but we already have similar cases + // where a parameter is annotated and if something in the method sets a specific known type to it + // we will also make it just work, even if the annotation doesn't match the usage. + AddReturnValue (new SystemTypeValue (staticType)); + } else { + // Make sure the type is marked (this will mark it as used via reflection, which is sort of true) + // This should already be true for most cases (method params, fields, ...), but just in case + _reflectionMarker.MarkType (_origin, staticType); - // To get good reporting of errors we need to track the origin of the value for all method calls - // but except Newobj as those are special. - if (GetReturnTypeWithoutModifiers (calledMethodDefinition.ReturnType).MetadataType != MetadataType.Void) { - methodReturnValue = CreateMethodReturnValue (calledMethodDefinition, returnValueDynamicallyAccessedMemberTypes); + var annotation = _markStep.DynamicallyAccessedMembersTypeHierarchy + .ApplyDynamicallyAccessedMembersToTypeHierarchy (_reflectionMarker, staticType); - return true; + // Return a value which is "unknown type" with annotation. For now we'll use the return value node + // for the method, which means we're loosing the information about which staticType this + // started with. For now we don't need it, but we can add it later on. + AddReturnValue (_annotations.GetMethodReturnValue (calledMethodDefinition, annotation)); + } } - - return false; } - } finally { - reflectionContext.Dispose (); + break; + + // Note about Activator.CreateInstance + // There are 2 interesting cases: + // - The generic argument for T is either specific type or annotated - in that case generic instantiation will handle this + // since from .NET 6+ the T is annotated with PublicParameterlessConstructor annotation, so the linker would apply this as for any other method. + // - The generic argument for T is unannotated type - the generic instantiantion handling has a special case for handling PublicParameterlessConstructor requirement + // in such that if the generic argument type has the "new" constraint it will not warn (as it is effectively the same thing semantically). + // For all other cases, the linker would have already produced a warning. + + default: + throw new NotImplementedException ("Unhandled instrinsic"); } // If we get here, we handled this as an intrinsic. As a convenience, if the code above // didn't set the return value (and the method has a return value), we will set it to be an // unknown value with the return type of the method. - if (methodReturnValue == null) { - if (GetReturnTypeWithoutModifiers (calledMethod.ReturnType).MetadataType != MetadataType.Void) { - methodReturnValue = CreateMethodReturnValue (calledMethodDefinition, returnValueDynamicallyAccessedMemberTypes); - } - } + bool returnsVoid = calledMethod.ReturnsVoid (); + methodReturnValue = maybeMethodReturnValue ?? (returnsVoid ? + MultiValueLattice.Top : + _annotations.GetMethodReturnValue (calledMethodDefinition, returnValueDynamicallyAccessedMemberTypes)); // Validate that the return value has the correct annotations as per the method return value annotations if (returnValueDynamicallyAccessedMemberTypes != 0) { - foreach (var uniqueValue in methodReturnValue.UniqueValues ()) { - if (uniqueValue is LeafValueWithDynamicallyAccessedMemberNode methodReturnValueWithMemberTypes) { + foreach (var uniqueValue in methodReturnValue) { + if (uniqueValue is ValueWithDynamicallyAccessedMembers methodReturnValueWithMemberTypes) { if (!methodReturnValueWithMemberTypes.DynamicallyAccessedMemberTypes.HasFlag (returnValueDynamicallyAccessedMemberTypes)) throw new InvalidOperationException ($"Internal linker error: processing of call from {callingMethodDefinition.GetDisplayName ()} to {calledMethod.GetDisplayName ()} returned value which is not correctly annotated with the expected dynamic member access kinds."); } else if (uniqueValue is SystemTypeValue) { @@ -1796,6 +432,11 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c } return true; + + void AddReturnValue (MultiValue value) + { + maybeMethodReturnValue = (maybeMethodReturnValue is null) ? value : MultiValueLattice.Meet ((MultiValue) maybeMethodReturnValue, value); + } } bool IsComInterop (IMarshalInfoProvider marshalInfoProvider, TypeReference parameterType) @@ -1822,10 +463,10 @@ bool IsComInterop (IMarshalInfoProvider marshalInfoProvider, TypeReference param var parameterTypeDef = _context.TryResolve (parameterType); if (parameterTypeDef != null) { - if (parameterTypeDef.IsTypeOf ("System", "Array")) { + if (parameterTypeDef.IsTypeOf (WellKnownType.System_Array)) { // System.Array marshals as IUnknown by default return true; - } else if (parameterTypeDef.IsTypeOf ("System", "String") || + } else if (parameterTypeDef.IsTypeOf (WellKnownType.System_String) || parameterTypeDef.IsTypeOf ("System.Text", "StringBuilder")) { // String and StringBuilder are special cased by interop return false; @@ -1856,609 +497,10 @@ bool IsComInterop (IMarshalInfoProvider marshalInfoProvider, TypeReference param return false; } - bool AnalyzeGenericInstantiationTypeArray (ValueNode? arrayParam, ref ReflectionPatternContext reflectionContext, MethodReference calledMethod, IList genericParameters) - { - bool hasRequirements = false; - foreach (var genericParameter in genericParameters) { - if (_context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (genericParameter) != DynamicallyAccessedMemberTypes.None) { - hasRequirements = true; - break; - } - } - - // If there are no requirements, then there's no point in warning - if (!hasRequirements) - return true; - - foreach (var typesValue in arrayParam.UniqueValues ()) { - if (typesValue.Kind != ValueNodeKind.Array) { - return false; - } - ArrayValue array = (ArrayValue) typesValue; - int? size = array.Size.AsConstInt (); - if (size == null || size != genericParameters.Count) { - return false; - } - bool allIndicesKnown = true; - for (int i = 0; i < size.Value; i++) { - if (!array.IndexValues.TryGetValue (i, out ValueBasicBlockPair value) || value.Value is null or { Kind: ValueNodeKind.Unknown }) { - allIndicesKnown = false; - break; - } - } - - if (!allIndicesKnown) { - return false; - } - - for (int i = 0; i < size.Value; i++) { - if (array.IndexValues.TryGetValue (i, out ValueBasicBlockPair value)) { - RequireDynamicallyAccessedMembers ( - ref reflectionContext, - _context.Annotations.FlowAnnotations.GetGenericParameterAnnotation (genericParameters[i]), - value.Value, - calledMethod.Resolve ()); - } - } - } - return true; - } - - void ProcessCreateInstanceByName (ref ReflectionPatternContext reflectionContext, MethodDefinition calledMethod, ValueNodeList methodParams) - { - reflectionContext.AnalyzingPattern (); - - BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; - bool parameterlessConstructor = true; - if (calledMethod.Parameters.Count == 8 && calledMethod.Parameters[2].ParameterType.MetadataType == MetadataType.Boolean) { - parameterlessConstructor = false; - bindingFlags = BindingFlags.Instance; - if (methodParams[3].AsConstInt () is int bindingFlagsInt) - bindingFlags |= (BindingFlags) bindingFlagsInt; - else - bindingFlags |= BindingFlags.Public | BindingFlags.NonPublic; - } - - int methodParamsOffset = calledMethod.HasImplicitThis () ? 1 : 0; - - foreach (var assemblyNameValue in methodParams[methodParamsOffset].UniqueValues ()) { - if (assemblyNameValue is KnownStringValue assemblyNameStringValue) { - foreach (var typeNameValue in methodParams[methodParamsOffset + 1].UniqueValues ()) { - if (typeNameValue is KnownStringValue typeNameStringValue) { - var resolvedAssembly = _context.TryResolve (assemblyNameStringValue.Contents); - if (resolvedAssembly == null) { - reflectionContext.RecordUnrecognizedPattern (2061, $"The assembly name '{assemblyNameStringValue.Contents}' passed to method '{calledMethod.GetDisplayName ()}' references assembly which is not available."); - continue; - } - - if (!_context.TypeNameResolver.TryResolveTypeName (resolvedAssembly, typeNameStringValue.Contents, out TypeReference? typeRef) - || _context.TryResolve (typeRef) is not TypeDefinition resolvedType - || typeRef is ArrayType) { - // It's not wrong to have a reference to non-existing type - the code may well expect to get an exception in this case - // Note that we did find the assembly, so it's not a linker config problem, it's either intentional, or wrong versions of assemblies - // but linker can't know that. In case a user tries to create an array using System.Activator we should simply ignore it, the user - // might expect an exception to be thrown. - reflectionContext.RecordHandledPattern (); - continue; - } - - MarkConstructorsOnType (ref reflectionContext, resolvedType, parameterlessConstructor ? m => m.Parameters.Count == 0 : null, bindingFlags); - } else { - reflectionContext.RecordUnrecognizedPattern (2032, $"Unrecognized value passed to the parameter '{calledMethod.Parameters[1].Name}' of method '{calledMethod.GetDisplayName ()}'. It's not possible to guarantee the availability of the target type."); - } - } - } else { - reflectionContext.RecordUnrecognizedPattern (2032, $"Unrecognized value passed to the parameter '{calledMethod.Parameters[0].Name}' of method '{calledMethod.GetDisplayName ()}'. It's not possible to guarantee the availability of the target type."); - } - } - } - - void ProcessGetMethodByName ( - ref ReflectionPatternContext reflectionContext, - TypeDefinition typeDefinition, - string methodName, - BindingFlags? bindingFlags, - ref ValueNode? methodReturnValue) - { - bool foundAny = false; - foreach (var method in typeDefinition.GetMethodsOnTypeHierarchy (_context, m => m.Name == methodName, bindingFlags)) { - MarkMethod (ref reflectionContext, method); - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemReflectionMethodBaseValue (method)); - foundAny = true; - } - - // If there were no methods found the API will return null at runtime, so we should - // track the null as a return value as well. - // This also prevents warnings in such case, since if we don't set the return value it will be - // "unknown" and consumers may warn. - if (!foundAny) - methodReturnValue = MergePointValue.MergeValues (methodReturnValue, NullValue.Instance); - } - - public static DynamicallyAccessedMemberTypes GetMissingMemberTypes (DynamicallyAccessedMemberTypes requiredMemberTypes, DynamicallyAccessedMemberTypes availableMemberTypes) - { - if (availableMemberTypes.HasFlag (requiredMemberTypes)) - return DynamicallyAccessedMemberTypes.None; - - if (requiredMemberTypes == DynamicallyAccessedMemberTypes.All) - return DynamicallyAccessedMemberTypes.All; - - var missingMemberTypes = requiredMemberTypes & ~availableMemberTypes; - - // PublicConstructors is a special case since its value is 3 - so PublicParameterlessConstructor (1) | _PublicConstructor_WithMoreThanOneParameter_ (2) - // The above bit logic only works for value with single bit set. - if (requiredMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors) && - !availableMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors)) - missingMemberTypes |= DynamicallyAccessedMemberTypes.PublicConstructors; - - return missingMemberTypes; - } - - string GetMemberTypesString (DynamicallyAccessedMemberTypes memberTypes) - { - Debug.Assert (memberTypes != DynamicallyAccessedMemberTypes.None); - - if (memberTypes == DynamicallyAccessedMemberTypes.All) - return $"'{nameof (DynamicallyAccessedMemberTypes)}.{nameof (DynamicallyAccessedMemberTypes.All)}'"; - - var memberTypesList = AllDynamicallyAccessedMemberTypes - .Where (damt => (memberTypes & damt) == damt && damt != DynamicallyAccessedMemberTypes.None) - .ToList (); - - if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors)) - memberTypesList.Remove (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor); - - return string.Join (", ", memberTypesList.Select (mt => { - string mtName = mt == DynamicallyAccessedMemberTypesOverlay.Interfaces - ? nameof (DynamicallyAccessedMemberTypesOverlay.Interfaces) - : mt.ToString (); - - return $"'{nameof (DynamicallyAccessedMemberTypes)}.{mtName}'"; - })); - } - - void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionContext, DynamicallyAccessedMemberTypes requiredMemberTypes, ValueNode? value, IMetadataTokenProvider targetContext) - { - foreach (var uniqueValue in value.UniqueValues ()) { - if (requiredMemberTypes == DynamicallyAccessedMemberTypes.PublicParameterlessConstructor - && uniqueValue is SystemTypeForGenericParameterValue genericParam - && genericParam.GenericParameter.HasDefaultConstructorConstraint) { - // We allow a new() constraint on a generic parameter to satisfy DynamicallyAccessedMemberTypes.PublicParameterlessConstructor - reflectionContext.RecordHandledPattern (); - } else if (uniqueValue is LeafValueWithDynamicallyAccessedMemberNode valueWithDynamicallyAccessedMember) { - var availableMemberTypes = valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes; - var missingMemberTypesValue = GetMissingMemberTypes (requiredMemberTypes, availableMemberTypes); - if (missingMemberTypesValue != DynamicallyAccessedMemberTypes.None) { - var missingMemberTypes = GetMemberTypesString (missingMemberTypesValue); - - switch ((valueWithDynamicallyAccessedMember.SourceContext, targetContext)) { - case (ParameterDefinition sourceParameter, ParameterDefinition targetParameter): - reflectionContext.RecordUnrecognizedPattern (2067, string.Format (Resources.Strings.IL2067, - DiagnosticUtilities.GetParameterNameForErrorMessage (targetParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName (targetParameter.Method), - DiagnosticUtilities.GetParameterNameForErrorMessage (sourceParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName (sourceParameter.Method), - missingMemberTypes)); - break; - case (ParameterDefinition sourceParameter, MethodReturnType targetMethodReturnType): - reflectionContext.RecordUnrecognizedPattern (2068, string.Format (Resources.Strings.IL2068, - DiagnosticUtilities.GetMethodSignatureDisplayName (targetMethodReturnType.Method), - DiagnosticUtilities.GetParameterNameForErrorMessage (sourceParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName (sourceParameter.Method), - missingMemberTypes)); - break; - case (ParameterDefinition sourceParameter, FieldDefinition targetField): - reflectionContext.RecordUnrecognizedPattern (2069, string.Format (Resources.Strings.IL2069, - targetField.GetDisplayName (), - DiagnosticUtilities.GetParameterNameForErrorMessage (sourceParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName (sourceParameter.Method), - missingMemberTypes)); - break; - case (ParameterDefinition sourceParameter, MethodDefinition targetMethod): - reflectionContext.RecordUnrecognizedPattern (2070, string.Format (Resources.Strings.IL2070, - targetMethod.GetDisplayName (), - DiagnosticUtilities.GetParameterNameForErrorMessage (sourceParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName (sourceParameter.Method), - missingMemberTypes)); - break; - case (ParameterDefinition sourceParameter, GenericParameter targetGenericParameter): - // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used - reflectionContext.RecordUnrecognizedPattern (2071, string.Format (Resources.Strings.IL2071, - targetGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (targetGenericParameter), - DiagnosticUtilities.GetParameterNameForErrorMessage (sourceParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName (sourceParameter.Method), - missingMemberTypes)); - break; - - case (MethodReturnType sourceMethodReturnType, ParameterDefinition targetParameter): - reflectionContext.RecordUnrecognizedPattern (2072, string.Format (Resources.Strings.IL2072, - DiagnosticUtilities.GetParameterNameForErrorMessage (targetParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName (targetParameter.Method), - DiagnosticUtilities.GetMethodSignatureDisplayName (sourceMethodReturnType.Method), - missingMemberTypes)); - break; - case (MethodReturnType sourceMethodReturnType, MethodReturnType targetMethodReturnType): - reflectionContext.RecordUnrecognizedPattern (2073, string.Format (Resources.Strings.IL2073, - DiagnosticUtilities.GetMethodSignatureDisplayName (targetMethodReturnType.Method), - DiagnosticUtilities.GetMethodSignatureDisplayName (sourceMethodReturnType.Method), - missingMemberTypes)); - break; - case (MethodReturnType sourceMethodReturnType, FieldDefinition targetField): - reflectionContext.RecordUnrecognizedPattern (2074, string.Format (Resources.Strings.IL2074, - targetField.GetDisplayName (), - DiagnosticUtilities.GetMethodSignatureDisplayName (sourceMethodReturnType.Method), - missingMemberTypes)); - break; - case (MethodReturnType sourceMethodReturnType, MethodDefinition targetMethod): - reflectionContext.RecordUnrecognizedPattern (2075, string.Format (Resources.Strings.IL2075, - targetMethod.GetDisplayName (), - DiagnosticUtilities.GetMethodSignatureDisplayName (sourceMethodReturnType.Method), - missingMemberTypes)); - break; - case (MethodReturnType sourceMethodReturnType, GenericParameter targetGenericParameter): - // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used - reflectionContext.RecordUnrecognizedPattern (2076, string.Format (Resources.Strings.IL2076, - targetGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (targetGenericParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName (sourceMethodReturnType.Method), - missingMemberTypes)); - break; - - case (FieldDefinition sourceField, ParameterDefinition targetParameter): - reflectionContext.RecordUnrecognizedPattern (2077, string.Format (Resources.Strings.IL2077, - DiagnosticUtilities.GetParameterNameForErrorMessage (targetParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName (targetParameter.Method), - sourceField.GetDisplayName (), - missingMemberTypes)); - break; - case (FieldDefinition sourceField, MethodReturnType targetMethodReturnType): - reflectionContext.RecordUnrecognizedPattern (2078, string.Format (Resources.Strings.IL2078, - DiagnosticUtilities.GetMethodSignatureDisplayName (targetMethodReturnType.Method), - sourceField.GetDisplayName (), - missingMemberTypes)); - break; - case (FieldDefinition sourceField, FieldDefinition targetField): - reflectionContext.RecordUnrecognizedPattern (2079, string.Format (Resources.Strings.IL2079, - targetField.GetDisplayName (), - sourceField.GetDisplayName (), - missingMemberTypes)); - break; - case (FieldDefinition sourceField, MethodDefinition targetMethod): - reflectionContext.RecordUnrecognizedPattern (2080, string.Format (Resources.Strings.IL2080, - targetMethod.GetDisplayName (), - sourceField.GetDisplayName (), - missingMemberTypes)); - break; - case (FieldDefinition sourceField, GenericParameter targetGenericParameter): - // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used - reflectionContext.RecordUnrecognizedPattern (2081, string.Format (Resources.Strings.IL2081, - targetGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (targetGenericParameter), - sourceField.GetDisplayName (), - missingMemberTypes)); - break; - - case (MethodDefinition sourceMethod, ParameterDefinition targetParameter): - reflectionContext.RecordUnrecognizedPattern (2082, string.Format (Resources.Strings.IL2082, - DiagnosticUtilities.GetParameterNameForErrorMessage (targetParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName (targetParameter.Method), - sourceMethod.GetDisplayName (), - missingMemberTypes)); - break; - case (MethodDefinition sourceMethod, MethodReturnType targetMethodReturnType): - reflectionContext.RecordUnrecognizedPattern (2083, string.Format (Resources.Strings.IL2083, - DiagnosticUtilities.GetMethodSignatureDisplayName (targetMethodReturnType.Method), - sourceMethod.GetDisplayName (), - missingMemberTypes)); - break; - case (MethodDefinition sourceMethod, FieldDefinition targetField): - reflectionContext.RecordUnrecognizedPattern (2084, string.Format (Resources.Strings.IL2084, - targetField.GetDisplayName (), - sourceMethod.GetDisplayName (), - missingMemberTypes)); - break; - case (MethodDefinition sourceMethod, MethodDefinition targetMethod): - reflectionContext.RecordUnrecognizedPattern (2085, string.Format (Resources.Strings.IL2085, - targetMethod.GetDisplayName (), - sourceMethod.GetDisplayName (), - missingMemberTypes)); - break; - case (MethodDefinition sourceMethod, GenericParameter targetGenericParameter): - // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used - reflectionContext.RecordUnrecognizedPattern (2086, string.Format (Resources.Strings.IL2086, - targetGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (targetGenericParameter), - sourceMethod.GetDisplayName (), - missingMemberTypes)); - break; - - case (GenericParameter sourceGenericParameter, ParameterDefinition targetParameter): - reflectionContext.RecordUnrecognizedPattern (2087, string.Format (Resources.Strings.IL2087, - DiagnosticUtilities.GetParameterNameForErrorMessage (targetParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName (targetParameter.Method), - sourceGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (sourceGenericParameter), - missingMemberTypes)); - break; - case (GenericParameter sourceGenericParameter, MethodReturnType targetMethodReturnType): - reflectionContext.RecordUnrecognizedPattern (2088, string.Format (Resources.Strings.IL2088, - DiagnosticUtilities.GetMethodSignatureDisplayName (targetMethodReturnType.Method), - sourceGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (sourceGenericParameter), - missingMemberTypes)); - break; - case (GenericParameter sourceGenericParameter, FieldDefinition targetField): - reflectionContext.RecordUnrecognizedPattern (2089, string.Format (Resources.Strings.IL2089, - targetField.GetDisplayName (), - sourceGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (sourceGenericParameter), - missingMemberTypes)); - break; - case (GenericParameter sourceGenericParameter, MethodDefinition targetMethod): - // Currently this is never generated, it might be possible one day if we try to validate annotations on results of reflection - // For example code like this should ideally one day generate the warning - // void TestMethod() - // { - // // This passes the T as the "this" parameter to Type.GetMethods() - // typeof(Type).GetMethod("GetMethods").Invoke(typeof(T), new object[] {}); - // } - reflectionContext.RecordUnrecognizedPattern (2090, string.Format (Resources.Strings.IL2090, - targetMethod.GetDisplayName (), - sourceGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (sourceGenericParameter), - missingMemberTypes)); - break; - case (GenericParameter sourceGenericParameter, GenericParameter targetGenericParameter): - reflectionContext.RecordUnrecognizedPattern (2091, string.Format (Resources.Strings.IL2091, - targetGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (targetGenericParameter), - sourceGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (sourceGenericParameter), - missingMemberTypes)); - break; - - default: - throw new NotImplementedException ($"unsupported source context {valueWithDynamicallyAccessedMember.SourceContext} or target context {targetContext}"); - }; - } else { - reflectionContext.RecordHandledPattern (); - } - } else if (uniqueValue is SystemTypeValue systemTypeValue) { - MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, systemTypeValue.TypeRepresented, requiredMemberTypes, DependencyKind.DynamicallyAccessedMember); - } else if (uniqueValue is KnownStringValue knownStringValue) { - if (!_context.TypeNameResolver.TryResolveTypeName (knownStringValue.Contents, reflectionContext.Source, out TypeReference? typeRef, out AssemblyDefinition? typeAssembly) - || ResolveToTypeDefinition (typeRef) is not TypeDefinition foundType) { - // Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. - reflectionContext.RecordHandledPattern (); - } else { - MarkType (ref reflectionContext, typeRef); - MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, foundType, requiredMemberTypes, DependencyKind.DynamicallyAccessedMember); - _context.MarkingHelpers.MarkMatchingExportedType (foundType, typeAssembly, new DependencyInfo (DependencyKind.DynamicallyAccessedMember, foundType), reflectionContext.Origin); - } - } else if (uniqueValue == NullValue.Instance) { - // Ignore - probably unreachable path as it would fail at runtime anyway. - } else { - switch (targetContext) { - case ParameterDefinition parameterDefinition: - reflectionContext.RecordUnrecognizedPattern ( - 2062, - $"Value passed to parameter '{DiagnosticUtilities.GetParameterNameForErrorMessage (parameterDefinition)}' of method '{DiagnosticUtilities.GetMethodSignatureDisplayName (parameterDefinition.Method)}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); - break; - case MethodReturnType methodReturnType: - reflectionContext.RecordUnrecognizedPattern ( - 2063, - $"Value returned from method '{DiagnosticUtilities.GetMethodSignatureDisplayName (methodReturnType.Method)}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); - break; - case FieldDefinition fieldDefinition: - reflectionContext.RecordUnrecognizedPattern ( - 2064, - $"Value assigned to {fieldDefinition.GetDisplayName ()} can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); - break; - case MethodDefinition methodDefinition: - reflectionContext.RecordUnrecognizedPattern ( - 2065, - $"Value passed to implicit 'this' parameter of method '{methodDefinition.GetDisplayName ()}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); - break; - case GenericParameter genericParameter: - // Unknown value to generic parameter - this is possible if the generic argument fails to resolve - reflectionContext.RecordUnrecognizedPattern ( - 2066, - $"Type passed to generic parameter '{genericParameter.Name}' of '{DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName (genericParameter)}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); - break; - default: throw new NotImplementedException ($"unsupported target context {targetContext.GetType ()}"); - }; - } - } - - reflectionContext.RecordHandledPattern (); - } - - static BindingFlags? GetBindingFlagsFromValue (ValueNode? parameter) => (BindingFlags?) parameter.AsConstInt (); - - static bool BindingFlagsAreUnsupported (BindingFlags? bindingFlags) - { - if (bindingFlags == null) - return true; - - // Binding flags we understand - const BindingFlags UnderstoodBindingFlags = - BindingFlags.DeclaredOnly | - BindingFlags.Instance | - BindingFlags.Static | - BindingFlags.Public | - BindingFlags.NonPublic | - BindingFlags.FlattenHierarchy | - BindingFlags.ExactBinding; - - // Binding flags that don't affect binding outside InvokeMember (that we don't analyze). - const BindingFlags IgnorableBindingFlags = - BindingFlags.InvokeMethod | - BindingFlags.CreateInstance | - BindingFlags.GetField | - BindingFlags.SetField | - BindingFlags.GetProperty | - BindingFlags.SetProperty; - - BindingFlags flags = bindingFlags.Value; - return (flags & ~(UnderstoodBindingFlags | IgnorableBindingFlags)) != 0; - } - - static bool HasBindingFlag (BindingFlags? bindingFlags, BindingFlags? search) => bindingFlags != null && (bindingFlags & search) == search; - - public void MarkTypeForDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionContext, TypeDefinition typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes, DependencyKind dependencyKind, bool declaredOnly = false) + void RequireDynamicallyAccessedMembers (in DiagnosticContext diagnosticContext, in MultiValue value, ValueWithDynamicallyAccessedMembers targetValue) { - foreach (var member in typeDefinition.GetDynamicallyAccessedMembers (_context, requiredMemberTypes, declaredOnly)) { - switch (member) { - case MethodDefinition method: - MarkMethod (ref reflectionContext, method, dependencyKind); - break; - case FieldDefinition field: - MarkField (ref reflectionContext, field, dependencyKind); - break; - case TypeDefinition nestedType: - MarkType (ref reflectionContext, nestedType, dependencyKind); - break; - case PropertyDefinition property: - MarkProperty (ref reflectionContext, property, dependencyKind); - break; - case EventDefinition @event: - MarkEvent (ref reflectionContext, @event, dependencyKind); - break; - case InterfaceImplementation interfaceImplementation: - MarkInterfaceImplementation (ref reflectionContext, interfaceImplementation, dependencyKind); - break; - } - } + var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction (_context, _reflectionMarker, diagnosticContext); + requireDynamicallyAccessedMembersAction.Invoke (value, targetValue); } - - void MarkType (ref ReflectionPatternContext reflectionContext, TypeReference typeReference, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) - { - var dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); - if (_context.TryResolve (typeReference) is TypeDefinition type) - reflectionContext.RecordRecognizedPattern (type, () => _markStep.MarkTypeVisibleToReflection (typeReference, type, dependencyInfo)); - } - - void MarkMethod (ref ReflectionPatternContext reflectionContext, MethodDefinition method, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) - { - var dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); - reflectionContext.RecordRecognizedPattern (method, () => _markStep.MarkMethodVisibleToReflection (method, dependencyInfo)); - } - - void MarkField (ref ReflectionPatternContext reflectionContext, FieldDefinition field, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) - { - var dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); - reflectionContext.RecordRecognizedPattern (field, () => _markStep.MarkFieldVisibleToReflection (field, dependencyInfo)); - } - - void MarkProperty (ref ReflectionPatternContext reflectionContext, PropertyDefinition property, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) - { - var dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); - reflectionContext.RecordRecognizedPattern (property, () => { _markStep.MarkPropertyVisibleToReflection (property, dependencyInfo); }); - } - - void MarkEvent (ref ReflectionPatternContext reflectionContext, EventDefinition @event, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) - { - var dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); - reflectionContext.RecordRecognizedPattern (@event, () => { _markStep.MarkEventVisibleToReflection (@event, dependencyInfo); }); - } - - void MarkInterfaceImplementation (ref ReflectionPatternContext reflectionContext, InterfaceImplementation interfaceImplementation, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) - { - var dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); - reflectionContext.RecordRecognizedPattern (interfaceImplementation, () => _markStep.MarkInterfaceImplementation (interfaceImplementation, null, dependencyInfo)); - } - - void MarkConstructorsOnType (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func? filter, BindingFlags? bindingFlags = null) - { - foreach (var ctor in type.GetConstructorsOnType (filter, bindingFlags)) - MarkMethod (ref reflectionContext, ctor); - } - - void MarkFieldsOnTypeHierarchy (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) - { - foreach (var field in type.GetFieldsOnTypeHierarchy (_context, filter, bindingFlags)) - MarkField (ref reflectionContext, field); - } - - TypeDefinition[]? MarkNestedTypesOnType (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) - { - var result = new ArrayBuilder (); - - foreach (var nestedType in type.GetNestedTypesOnType (filter, bindingFlags)) { - result.Add (nestedType); - MarkType (ref reflectionContext, nestedType); - } - - return result.ToArray (); - } - - void MarkPropertiesOnTypeHierarchy (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) - { - foreach (var property in type.GetPropertiesOnTypeHierarchy (_context, filter, bindingFlags)) - MarkProperty (ref reflectionContext, property); - } - - void MarkEventsOnTypeHierarchy (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) - { - foreach (var @event in type.GetEventsOnTypeHierarchy (_context, filter, bindingFlags)) - MarkEvent (ref reflectionContext, @event); - } - - void ValidateGenericMethodInstantiation ( - ref ReflectionPatternContext reflectionContext, - MethodDefinition genericMethod, - ValueNode? genericParametersArray, - MethodReference reflectionMethod) - { - if (!genericMethod.HasGenericParameters) { - reflectionContext.RecordHandledPattern (); - return; - } - - if (!AnalyzeGenericInstantiationTypeArray (genericParametersArray, ref reflectionContext, reflectionMethod, genericMethod.GenericParameters)) { - reflectionContext.RecordUnrecognizedPattern ((int) DiagnosticId.MakeGenericMethod, - new DiagnosticString (DiagnosticId.MakeGenericMethod).GetMessage (DiagnosticUtilities.GetMethodSignatureDisplayName (reflectionMethod))); - } else { - reflectionContext.RecordHandledPattern (); - } - } - - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (BindingFlags? bindingFlags) => - (HasBindingFlag (bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicNestedTypes : DynamicallyAccessedMemberTypes.None) | - (HasBindingFlag (bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicNestedTypes : DynamicallyAccessedMemberTypes.None) | - (BindingFlagsAreUnsupported (bindingFlags) ? DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes : DynamicallyAccessedMemberTypes.None); - - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (BindingFlags? bindingFlags) => - (HasBindingFlag (bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicConstructors : DynamicallyAccessedMemberTypes.None) | - (HasBindingFlag (bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicConstructors : DynamicallyAccessedMemberTypes.None) | - (BindingFlagsAreUnsupported (bindingFlags) ? DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors : DynamicallyAccessedMemberTypes.None); - - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (BindingFlags? bindingFlags) => - (HasBindingFlag (bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicMethods : DynamicallyAccessedMemberTypes.None) | - (HasBindingFlag (bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None) | - (BindingFlagsAreUnsupported (bindingFlags) ? DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None); - - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields (BindingFlags? bindingFlags) => - (HasBindingFlag (bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicFields : DynamicallyAccessedMemberTypes.None) | - (HasBindingFlag (bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None) | - (BindingFlagsAreUnsupported (bindingFlags) ? DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None); - - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties (BindingFlags? bindingFlags) => - (HasBindingFlag (bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicProperties : DynamicallyAccessedMemberTypes.None) | - (HasBindingFlag (bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None) | - (BindingFlagsAreUnsupported (bindingFlags) ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None); - - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents (BindingFlags? bindingFlags) => - (HasBindingFlag (bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicEvents : DynamicallyAccessedMemberTypes.None) | - (HasBindingFlag (bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None) | - (BindingFlagsAreUnsupported (bindingFlags) ? DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None); - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers (BindingFlags? bindingFlags) => - GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors (bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents (bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields (bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods (bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties (bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes (bindingFlags); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionPatternContext.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionPatternContext.cs deleted file mode 100644 index 1cd64ce438f44..0000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ReflectionPatternContext.cs +++ /dev/null @@ -1,106 +0,0 @@ -// 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; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace Mono.Linker.Dataflow -{ - /// - /// Helper struct to pass around context information about reflection pattern - /// as a single parameter (and have a way to extend this in the future if we need to easily). - /// Also implements a simple validation mechanism to check that the code does report patter recognition - /// results for all methods it works on. - /// The promise of the pattern recorder is that for a given reflection method, it will either not talk - /// about it ever, or it will always report recognized/unrecognized. - /// - struct ReflectionPatternContext : IDisposable - { - readonly LinkContext _context; -#if DEBUG - bool _patternAnalysisAttempted; - bool _patternReported; -#endif - - public MessageOrigin Origin { get; init; } - public ICustomAttributeProvider? Source { get => Origin.Provider; } - public IMetadataTokenProvider MemberWithRequirements { get; init; } - public Instruction? Instruction { get; init; } - public bool ReportingEnabled { get; init; } - - public ReflectionPatternContext ( - LinkContext context, - bool reportingEnabled, - in MessageOrigin origin, - IMetadataTokenProvider memberWithRequirements, - Instruction? instruction = null) - { - _context = context; - ReportingEnabled = reportingEnabled; - Origin = origin; - MemberWithRequirements = memberWithRequirements; - Instruction = instruction; - -#if DEBUG - _patternAnalysisAttempted = false; - _patternReported = false; -#endif - } - -#pragma warning disable CA1822 - [Conditional ("DEBUG")] - public void AnalyzingPattern () - { -#if DEBUG - _patternAnalysisAttempted = true; -#endif - } - - [Conditional ("DEBUG")] - public void RecordHandledPattern () - { -#if DEBUG - _patternReported = true; -#endif - } -#pragma warning restore CA1822 - - public void RecordRecognizedPattern (IMetadataTokenProvider accessedItem, Action mark) - { -#if DEBUG - if (!_patternAnalysisAttempted) - throw new InvalidOperationException ($"Internal error: To correctly report all patterns, when starting to analyze a pattern the AnalyzingPattern must be called first. {Source} -> {MemberWithRequirements}"); - - _patternReported = true; -#endif - - mark (); - - if (ReportingEnabled) - _context.ReflectionPatternRecorder.RecognizedReflectionAccessPattern (Source, Instruction, accessedItem); - } - - public void RecordUnrecognizedPattern (int messageCode, string message) - { -#if DEBUG - if (!_patternAnalysisAttempted) - throw new InvalidOperationException ($"Internal error: To correctly report all patterns, when starting to analyze a pattern the AnalyzingPattern must be called first. {Source} -> {MemberWithRequirements}"); - - _patternReported = true; -#endif - - if (ReportingEnabled) - _context.ReflectionPatternRecorder.UnrecognizedReflectionAccessPattern (Origin, Source, Instruction, MemberWithRequirements, message, messageCode); - } - - public void Dispose () - { -#if DEBUG - if (_patternAnalysisAttempted && !_patternReported) - throw new InvalidOperationException ($"Internal error: A reflection pattern was analyzed, but no result was reported. {Source} -> {MemberWithRequirements}"); -#endif - } - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/RequireDynamicallyAccessedMembersAction.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/RequireDynamicallyAccessedMembersAction.cs new file mode 100644 index 0000000000000..d2fe33783b6ee --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/RequireDynamicallyAccessedMembersAction.cs @@ -0,0 +1,43 @@ +// 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.Diagnostics.CodeAnalysis; +using ILLink.Shared.TypeSystemProxy; +using Mono.Cecil; +using Mono.Linker; +using Mono.Linker.Dataflow; + +namespace ILLink.Shared.TrimAnalysis +{ + partial struct RequireDynamicallyAccessedMembersAction + { + readonly LinkContext _context; + readonly ReflectionMarker _reflectionMarker; + + public RequireDynamicallyAccessedMembersAction ( + LinkContext context, + ReflectionMarker reflectionMarker, + in DiagnosticContext diagnosticContext) + { + _context = context; + _reflectionMarker = reflectionMarker; + _diagnosticContext = diagnosticContext; + } + + public partial bool TryResolveTypeNameAndMark (string typeName, bool needsAssemblyName, out TypeProxy type) + { + if (_reflectionMarker.TryResolveTypeNameAndMark (typeName, _diagnosticContext.Origin, needsAssemblyName, out TypeDefinition? foundType)) { + type = new (foundType); + return true; + } else { + type = default; + return false; + } + } + + private partial void MarkTypeForDynamicallyAccessedMembers (in TypeProxy type, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + _reflectionMarker.MarkTypeForDynamicallyAccessedMembers (_diagnosticContext.Origin, type.Type, dynamicallyAccessedMemberTypes, DependencyKind.DynamicallyAccessedMember); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ScannerExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ScannerExtensions.cs index 0b7a2af84219e..166a968b66aed 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ScannerExtensions.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ScannerExtensions.cs @@ -1,5 +1,5 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// 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.Collections.Generic; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/SingleValueExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/SingleValueExtensions.cs new file mode 100644 index 0000000000000..c0024c1ccc132 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/SingleValueExtensions.cs @@ -0,0 +1,87 @@ +// 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.Collections.Generic; +using ILLink.Shared.DataFlow; +using Mono.Linker.Dataflow; + +namespace ILLink.Shared.TrimAnalysis +{ + // These are extension methods because we want to allow the use of them on null 'this' pointers. + internal static class SingleValueExtensions + { + /// + /// Returns true if a ValueNode graph contains a cycle + /// + /// Node to evaluate + /// Set of nodes previously seen on the current arc. Callers may pass a non-empty set + /// to test whether adding that set to this node would create a cycle. Contents will be modified by the walk + /// and should not be used by the caller after returning + /// Optional. The set of all nodes encountered during a walk after DetectCycle returns + /// + public static bool DetectCycle (this SingleValue node, HashSet seenNodes, HashSet? allNodesSeen) + { + if (node == null) + return false; + + if (seenNodes.Contains (node)) + return true; + + seenNodes.Add (node); + + if (allNodesSeen != null) { + allNodesSeen.Add (node); + } + + bool foundCycle = false; + switch (node) { + // + // Leaf nodes + // + case UnknownValue: + case NullValue: + case SystemTypeValue: + case RuntimeTypeHandleValue: + case KnownStringValue: + case ConstIntValue: + case MethodParameterValue: + case MethodThisParameterValue: + case MethodReturnValue: + case GenericParameterValue: + case RuntimeTypeHandleForGenericParameterValue: + case SystemReflectionMethodBaseValue: + case RuntimeMethodHandleValue: + case FieldValue: + break; + + // + // Nodes with children + // + case ArrayValue: + ArrayValue av = (ArrayValue) node; + foundCycle = av.Size.DetectCycle (seenNodes, allNodesSeen); + foreach (ValueBasicBlockPair pair in av.IndexValues.Values) { + foreach (var v in pair.Value) { + foundCycle |= v.DetectCycle (seenNodes, allNodesSeen); + } + } + break; + + case RuntimeTypeHandleForNullableValueWithDynamicallyAccessedMembers value: + foundCycle = value.UnderlyingTypeValue.DetectCycle (seenNodes, allNodesSeen); + break; + + case NullableValueWithDynamicallyAccessedMembers value: + foundCycle = value.UnderlyingTypeValue.DetectCycle (seenNodes, allNodesSeen); + break; + + default: + throw new Exception (String.Format ("Unknown node type: {0}", node.GetType ().Name)); + } + seenNodes.Remove (node); + + return foundCycle; + } + } +} \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/TypeProxy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/TypeProxy.cs new file mode 100644 index 0000000000000..52a43ec6a4935 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/TypeProxy.cs @@ -0,0 +1,43 @@ +// 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.Collections.Immutable; +using Mono.Cecil; +using Mono.Linker; + +namespace ILLink.Shared.TypeSystemProxy +{ + internal readonly partial struct TypeProxy + { + public TypeProxy (TypeDefinition type) => Type = type; + + public static implicit operator TypeProxy (TypeDefinition type) => new (type); + + internal partial ImmutableArray GetGenericParameters () + { + if (!Type.HasGenericParameters) + return ImmutableArray.Empty; + + var builder = ImmutableArray.CreateBuilder (Type.GenericParameters.Count); + foreach (var genericParameter in Type.GenericParameters) { + builder.Add (new GenericParameterProxy (genericParameter)); + } + + return builder.ToImmutableArray (); + } + + public TypeDefinition Type { get; } + + public string Name { get => Type.Name; } + + public string? Namespace { get => Type.Namespace; } + + public bool IsTypeOf (string @namespace, string name) => Type.IsTypeOf (@namespace, name); + + public bool IsTypeOf (WellKnownType wellKnownType) => Type.IsTypeOf (wellKnownType); + + public string GetDisplayName () => Type.GetDisplayName (); + + public override string ToString () => Type.ToString (); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ValueNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ValueNode.cs index 5e3349db6f860..5a0df8f347652 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ValueNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReferenceSource/ValueNode.cs @@ -1,1295 +1,13 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// 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.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text; -using Mono.Cecil; -using FieldDefinition = Mono.Cecil.FieldDefinition; -using GenericParameter = Mono.Cecil.GenericParameter; -using TypeDefinition = Mono.Cecil.TypeDefinition; +using MultiValue = ILLink.Shared.DataFlow.ValueSet; namespace Mono.Linker.Dataflow { - public enum ValueNodeKind - { - Invalid, // in case the Kind field is not initialized properly - - Unknown, // unknown value, has StaticType from context - - Null, // known value - SystemType, // known value - TypeRepresented - RuntimeTypeHandle, // known value - TypeRepresented - KnownString, // known value - Contents - ConstInt, // known value - Int32 - AnnotatedString, // string with known annotation - - MethodParameter, // symbolic placeholder - MethodReturn, // symbolic placeholder - - RuntimeMethodHandle, // known value - MethodRepresented - SystemReflectionMethodBase, // known value - MethodRepresented - - RuntimeTypeHandleForGenericParameter, // symbolic placeholder for generic parameter - SystemTypeForGenericParameter, // symbolic placeholder for generic parameter - - MergePoint, // structural, multiplexer - Values - GetTypeFromString, // structural, could be known value - KnownString - Array, // structural, could be known value - Array - - LoadField, // structural, could be known value - InstanceValue - } - - /// - /// A ValueNode represents a value in the IL dataflow analysis. It may not contain complete information as it is a - /// best-effort representation. Additionally, as the analysis is linear and does not account for control flow, any - /// given ValueNode may represent multiple values simultaneously. (This occurs, for example, at control flow join - /// points when both paths yield values on the IL stack or in a local.) - /// - public abstract class ValueNode : IEquatable - { - public ValueNode () - { -#if false // Helpful for debugging a cycle that has inadvertently crept into the graph - if (this.DetectCycle(new HashSet())) - { - throw new Exception("Found a cycle"); - } -#endif - } - - /// - /// The 'kind' of value node -- this represents the most-derived type and allows us to switch over and do - /// equality checks without the cost of casting. Intermediate non-leaf types in the ValueNode hierarchy should - /// be abstract. - /// - public ValueNodeKind Kind { get; protected set; } - - /// - /// The IL type of the value, represented as closely as possible, but not always exact. It can be null, for - /// example, when the analysis is imprecise or operating on malformed IL. - /// - public TypeDefinition? StaticType { get; protected set; } - - /// - /// Allows the enumeration of the direct children of this node. The ChildCollection struct returned here - /// supports 'foreach' without allocation. - /// - public ChildCollection Children { get { return new ChildCollection (this); } } - - /// - /// This property allows you to enumerate all 'unique values' represented by a given ValueNode. The basic idea - /// is that there will be no MergePointValues in the returned ValueNodes and all structural operations will be - /// applied so that each 'unique value' can be considered on its own without regard to the structure that led to - /// it. - /// - public UniqueValueCollection UniqueValuesInternal { - get { - return new UniqueValueCollection (this); - } - } - - /// - /// This protected method is how nodes implement the UniqueValues property. It is protected because it returns - /// an IEnumerable and we want to avoid allocating an enumerator for the exceedingly common case of there being - /// only one value in the enumeration. The UniqueValueCollection returned by the UniqueValues property handles - /// this detail. - /// - protected abstract IEnumerable EvaluateUniqueValues (); - - /// - /// RepresentsExactlyOneValue is used by the UniqueValues property to allow us to bypass allocating an - /// enumerator to return just one value. If a node returns 'true' from RepresentsExactlyOneValue, it must also - /// return that one value from GetSingleUniqueValue. If it always returns 'false', it doesn't need to implement - /// GetSingleUniqueValue. - /// - protected virtual bool RepresentsExactlyOneValue { get { return false; } } - - /// - /// GetSingleUniqueValue is called if, and only if, RepresentsExactlyOneValue returns true. It allows us to - /// bypass the allocation of an enumerator for the common case of returning exactly one value. - /// - protected virtual ValueNode GetSingleUniqueValue () - { - // Not implemented because RepresentsExactlyOneValue returns false and, therefore, this method should be - // unreachable. - throw new NotImplementedException (); - } - - protected abstract int NumChildren { get; } - protected abstract ValueNode ChildAt (int index); - - public virtual bool Equals (ValueNode? other) - { - return other != null && this.Kind == other.Kind && this.StaticType == other.StaticType; - } - - public abstract override int GetHashCode (); - - /// - /// Each node type must implement this to stringize itself. The expectation is that it is implemented using - /// ValueNodeDump.ValueNodeToString(), passing any non-ValueNode properties of interest (e.g. - /// SystemTypeValue.TypeRepresented). Properties that are invariant on a particular node type - /// should be omitted for clarity. - /// - protected abstract string NodeToString (); - - public override string ToString () - { - return NodeToString (); - } - - public override bool Equals (object? other) - { - if (!(other is ValueNode)) - return false; - - return this.Equals ((ValueNode) other); - } - - #region Specialized Collection Nested Types - /// - /// ChildCollection struct is used to wrap the operations on a node involving its children. In particular, the - /// struct implements a GetEnumerator method that is used to allow "foreach (ValueNode node in myNode.Children)" - /// without heap allocations. - /// - public struct ChildCollection : IEnumerable - { - /// - /// Enumerator for children of a ValueNode. Allows foreach(var child in node.Children) to work without - /// allocating a heap-based enumerator. - /// - public struct Enumerator : IEnumerator - { - int _index; - readonly ValueNode _parent; - - public Enumerator (ValueNode parent) - { - _parent = parent; - _index = -1; - } - - public ValueNode Current { get { return _parent.ChildAt (_index); } } - - object System.Collections.IEnumerator.Current { get { return Current; } } - - public bool MoveNext () - { - _index++; - return (_parent != null) ? (_index < _parent.NumChildren) : false; - } - - public void Reset () - { - _index = -1; - } - - public void Dispose () - { - } - } - - readonly ValueNode _parentNode; - - public ChildCollection (ValueNode parentNode) { _parentNode = parentNode; } - - // Used by C# 'foreach', when strongly typed, to avoid allocation. - public Enumerator GetEnumerator () - { - return new Enumerator (_parentNode); - } - - IEnumerator IEnumerable.GetEnumerator () - { - // note the boxing! - return new Enumerator (_parentNode); - } - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () - { - // note the boxing! - return new Enumerator (_parentNode); - } - - public int Count { get { return (_parentNode != null) ? _parentNode.NumChildren : 0; } } - } - - /// - /// UniqueValueCollection is used to wrap calls to ValueNode.EvaluateUniqueValues. If a ValueNode represents - /// only one value, then foreach(ValueNode value in node.UniqueValues) will not allocate a heap-based enumerator. - /// - /// This is implented by having each ValueNode tell us whether or not it represents exactly one value or not. - /// If it does, we fetch it with ValueNode.GetSingleUniqueValue(), otherwise, we fall back to the usual heap- - /// based IEnumerable returned by ValueNode.EvaluateUniqueValues. - /// - public struct UniqueValueCollection : IEnumerable - { - readonly IEnumerable? _multiValueEnumerable; - readonly ValueNode? _treeNode; - - public UniqueValueCollection (ValueNode node) - { - if (node.RepresentsExactlyOneValue) { - _multiValueEnumerable = null; - _treeNode = node; - } else { - _multiValueEnumerable = node.EvaluateUniqueValues (); - _treeNode = null; - } - } - - public Enumerator GetEnumerator () - { - return new Enumerator (_treeNode, _multiValueEnumerable); - } - - IEnumerator IEnumerable.GetEnumerator () - { - if (_multiValueEnumerable != null) { - return _multiValueEnumerable.GetEnumerator (); - } - - // note the boxing! - return GetEnumerator (); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () - { - if (_multiValueEnumerable != null) { - return _multiValueEnumerable.GetEnumerator (); - } - - // note the boxing! - return GetEnumerator (); - } - - - public struct Enumerator : IEnumerator - { - readonly IEnumerator? _multiValueEnumerator; - readonly ValueNode? _singleValueNode; - int _index; - - public Enumerator (ValueNode? treeNode, IEnumerable? multiValueEnumerable) - { - Debug.Assert (treeNode != null || multiValueEnumerable != null); - _singleValueNode = treeNode?.GetSingleUniqueValue (); - _multiValueEnumerator = multiValueEnumerable?.GetEnumerator (); - _index = -1; - } - - public void Reset () - { - if (_multiValueEnumerator != null) { - _multiValueEnumerator.Reset (); - return; - } - - _index = -1; - } - - public bool MoveNext () - { - if (_multiValueEnumerator != null) - return _multiValueEnumerator.MoveNext (); - - _index++; - return _index == 0; - } - - public ValueNode Current { - get { - if (_multiValueEnumerator != null) - return _multiValueEnumerator.Current; - - if (_index == 0) - return _singleValueNode!; - - throw new InvalidOperationException (); - } - } - - object System.Collections.IEnumerator.Current { get { return Current; } } - - public void Dispose () - { - } - } - } - #endregion - } - - /// - /// LeafValueNode represents a 'leaf' in the expression tree. In other words, the node has no ValueNode children. - /// It *may* still have non-ValueNode 'properties' that are interesting. This class serves, primarily, as a way to - /// collect up the very common implmentation of NumChildren/ChildAt for leaf nodes and the "represents exactly one - /// value" optimization. These things aren't on the ValueNode base class because, otherwise, new node types - /// deriving from ValueNode may 'forget' to implement these things. So this class allows them to remain abstract in - /// ValueNode while still having a common implementation for all the leaf nodes. - /// - public abstract class LeafValueNode : ValueNode - { - protected override int NumChildren { get { return 0; } } - protected override ValueNode ChildAt (int index) { throw new InvalidOperationException (); } - - protected override bool RepresentsExactlyOneValue { get { return true; } } - - protected override ValueNode GetSingleUniqueValue () { return this; } - - - protected override IEnumerable EvaluateUniqueValues () - { - // Leaf values should not represent more than one value. This method should be unreachable as long as - // RepresentsExactlyOneValue returns true. - throw new NotImplementedException (); - } - } - - // These are extension methods because we want to allow the use of them on null 'this' pointers. - internal static class ValueNodeExtensions - { - /// - /// Returns true if a ValueNode graph contains a cycle - /// - /// Node to evaluate - /// Set of nodes previously seen on the current arc. Callers may pass a non-empty set - /// to test whether adding that set to this node would create a cycle. Contents will be modified by the walk - /// and should not be used by the caller after returning - /// Optional. The set of all nodes encountered during a walk after DetectCycle returns - /// - public static bool DetectCycle (this ValueNode? node, HashSet seenNodes, HashSet? allNodesSeen) - { - if (node == null) - return false; - - if (seenNodes.Contains (node)) - return true; - - seenNodes.Add (node); - - if (allNodesSeen != null) { - allNodesSeen.Add (node); - } - - bool foundCycle = false; - switch (node.Kind) { - // - // Leaf nodes - // - case ValueNodeKind.Unknown: - case ValueNodeKind.Null: - case ValueNodeKind.SystemType: - case ValueNodeKind.RuntimeTypeHandle: - case ValueNodeKind.KnownString: - case ValueNodeKind.AnnotatedString: - case ValueNodeKind.ConstInt: - case ValueNodeKind.MethodParameter: - case ValueNodeKind.MethodReturn: - case ValueNodeKind.SystemTypeForGenericParameter: - case ValueNodeKind.RuntimeTypeHandleForGenericParameter: - case ValueNodeKind.SystemReflectionMethodBase: - case ValueNodeKind.RuntimeMethodHandle: - case ValueNodeKind.LoadField: - break; - - // - // Nodes with children - // - case ValueNodeKind.MergePoint: - foreach (ValueNode val in ((MergePointValue) node).Values) { - if (val.DetectCycle (seenNodes, allNodesSeen)) { - foundCycle = true; - } - } - break; - - case ValueNodeKind.GetTypeFromString: - GetTypeFromStringValue gtfsv = (GetTypeFromStringValue) node; - foundCycle = gtfsv.AssemblyIdentity.DetectCycle (seenNodes, allNodesSeen); - foundCycle |= gtfsv.NameString.DetectCycle (seenNodes, allNodesSeen); - break; - - case ValueNodeKind.Array: - ArrayValue av = (ArrayValue) node; - foundCycle = av.Size.DetectCycle (seenNodes, allNodesSeen); - foreach (ValueBasicBlockPair pair in av.IndexValues.Values) { - foundCycle |= pair.Value.DetectCycle (seenNodes, allNodesSeen); - } - break; - - default: - throw new Exception (String.Format ("Unknown node kind: {0}", node.Kind)); - } - seenNodes.Remove (node); - - return foundCycle; - } - - public static ValueNode.UniqueValueCollection UniqueValues (this ValueNode? node) - { - if (node == null) - return new ValueNode.UniqueValueCollection (UnknownValue.Instance); - - return node.UniqueValuesInternal; - } - - public static int? AsConstInt (this ValueNode? node) - { - if (node is ConstIntValue constInt) - return constInt.Value; - return null; - } - } - - internal static class ValueNodeDump - { - internal static string ValueNodeToString (ValueNode? node, params object[] args) - { - if (node == null) - return ""; - - StringBuilder sb = new StringBuilder (); - sb.Append (node.Kind.ToString ()); - sb.Append ("("); - if (args != null) { - for (int i = 0; i < args.Length; i++) { - if (i > 0) - sb.Append (","); - sb.Append (args[i] == null ? "" : args[i].ToString ()); - } - } - sb.Append (")"); - return sb.ToString (); - } - - static string GetIndent (int level) - { - StringBuilder sb = new StringBuilder (level * 2); - for (int i = 0; i < level; i++) - sb.Append (" "); - return sb.ToString (); - } - - public static void DumpTree (this ValueNode node, System.IO.TextWriter? writer = null, int indentLevel = 0) - { - if (writer == null) - writer = Console.Out; - - writer.Write (GetIndent (indentLevel)); - if (node == null) { - writer.WriteLine (""); - return; - } - - writer.WriteLine (node); - foreach (ValueNode child in node.Children) { - child.DumpTree (writer, indentLevel + 1); - } - } - } - - /// - /// Represents an unknown value. - /// - class UnknownValue : LeafValueNode - { - private UnknownValue () - { - Kind = ValueNodeKind.Unknown; - StaticType = null; - } - - public static UnknownValue Instance { get; } = new UnknownValue (); - - public override bool Equals (ValueNode? other) - { - return base.Equals (other); - } - - public override int GetHashCode () - { - // All instances of UnknownValue are equivalent, so they all hash to the same hashcode. This one was - // chosen for no particular reason at all. - return 0x98052; - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this); - } - } - - class NullValue : LeafValueNode - { - private NullValue () - { - Kind = ValueNodeKind.Null; - StaticType = null; - } - - public override bool Equals (ValueNode? other) - { - return base.Equals (other); - } - - public static NullValue Instance { get; } = new NullValue (); - - public override int GetHashCode () - { - // All instances of NullValue are equivalent, so they all hash to the same hashcode. This one was - // chosen for no particular reason at all. - return 0x90210; - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this); - } - } - - /// - /// This is a known System.Type value. TypeRepresented is the 'value' of the System.Type. - /// - class SystemTypeValue : LeafValueNode - { - public SystemTypeValue (TypeDefinition typeRepresented) - { - Kind = ValueNodeKind.SystemType; - - // Should be System.Type - but we don't have any use case where tracking it like that would matter - StaticType = null; - - TypeRepresented = typeRepresented; - } - - public TypeDefinition TypeRepresented { get; private set; } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - return Equals (this.TypeRepresented, ((SystemTypeValue) other).TypeRepresented); - } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, TypeRepresented); - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this, TypeRepresented); - } - } - - /// - /// This is the System.RuntimeTypeHandle equivalent to a node. - /// - class RuntimeTypeHandleValue : LeafValueNode - { - public RuntimeTypeHandleValue (TypeDefinition typeRepresented) - { - Kind = ValueNodeKind.RuntimeTypeHandle; - - // Should be System.RuntimeTypeHandle, but we don't have a use case for it like that - StaticType = null; - - TypeRepresented = typeRepresented; - } - - public TypeDefinition TypeRepresented { get; } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - return Equals (this.TypeRepresented, ((RuntimeTypeHandleValue) other).TypeRepresented); - } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, TypeRepresented); - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this, TypeRepresented); - } - } - - /// - /// This is a System.Type value which represents generic parameter (basically result of typeof(T)) - /// Its actual type is unknown, but it can have annotations. - /// - class SystemTypeForGenericParameterValue : LeafValueWithDynamicallyAccessedMemberNode - { - public SystemTypeForGenericParameterValue (GenericParameter genericParameter, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - : base (genericParameter) - { - Kind = ValueNodeKind.SystemTypeForGenericParameter; - - // Should be System.Type, but we don't have a use case for it - StaticType = null; - - GenericParameter = genericParameter; - DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; - } - - public GenericParameter GenericParameter { get; } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - var otherValue = (SystemTypeForGenericParameterValue) other; - return this.GenericParameter == otherValue.GenericParameter && this.DynamicallyAccessedMemberTypes == otherValue.DynamicallyAccessedMemberTypes; - } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, GenericParameter, DynamicallyAccessedMemberTypes); - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this, GenericParameter, DynamicallyAccessedMemberTypes); - } - } - - /// - /// This is the System.RuntimeTypeHandle equivalent to a node. - /// - class RuntimeTypeHandleForGenericParameterValue : LeafValueNode - { - public RuntimeTypeHandleForGenericParameterValue (GenericParameter genericParameter) - { - Kind = ValueNodeKind.RuntimeTypeHandleForGenericParameter; - - // Should be System.RuntimeTypeHandle, but we don't have a use case for it - StaticType = null; - - GenericParameter = genericParameter; - } - - public GenericParameter GenericParameter { get; } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - return Equals (this.GenericParameter, ((RuntimeTypeHandleForGenericParameterValue) other).GenericParameter); - } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, GenericParameter); - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this, GenericParameter); - } - } - - /// - /// This is the System.RuntimeMethodHandle equivalent to a node. - /// - class RuntimeMethodHandleValue : LeafValueNode - { - public RuntimeMethodHandleValue (MethodDefinition methodRepresented) - { - Kind = ValueNodeKind.RuntimeMethodHandle; - - // Should be System.RuntimeMethodHandle, but we don't have a use case for it - StaticType = null; - - MethodRepresented = methodRepresented; - } - - public MethodDefinition MethodRepresented { get; } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - return Equals (this.MethodRepresented, ((RuntimeMethodHandleValue) other).MethodRepresented); - } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, MethodRepresented); - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this, MethodRepresented); - } - } - - /// - /// This is a known System.Reflection.MethodBase value. MethodRepresented is the 'value' of the MethodBase. - /// - class SystemReflectionMethodBaseValue : LeafValueNode - { - public SystemReflectionMethodBaseValue (MethodDefinition methodRepresented) - { - Kind = ValueNodeKind.SystemReflectionMethodBase; - - // Should be System.Reflection.MethodBase, but we don't have a use case for it - StaticType = null; - - MethodRepresented = methodRepresented; - } - - public MethodDefinition MethodRepresented { get; private set; } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - return Equals (this.MethodRepresented, ((SystemReflectionMethodBaseValue) other).MethodRepresented); - } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, MethodRepresented); - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this, MethodRepresented); - } - } - - /// - /// A known string - such as the result of a ldstr. - /// - class KnownStringValue : LeafValueNode - { - public KnownStringValue (string contents) - { - Kind = ValueNodeKind.KnownString; - - // Should be System.String, but we don't have a use case for it - StaticType = null; - - Contents = contents; - } - - public string Contents { get; private set; } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - return this.Contents == ((KnownStringValue) other).Contents; - } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, Contents); - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this, "\"" + Contents + "\""); - } - } - - /// - /// Base class for all nodes which can have dynamically accessed member annotation. - /// - abstract class LeafValueWithDynamicallyAccessedMemberNode : LeafValueNode - { - public LeafValueWithDynamicallyAccessedMemberNode (IMetadataTokenProvider sourceContext) - { - SourceContext = sourceContext; - } - - public IMetadataTokenProvider SourceContext { get; private set; } - - /// - /// The bitfield of dynamically accessed member types the node guarantees - /// - public DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; protected set; } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - var otherValue = (LeafValueWithDynamicallyAccessedMemberNode) other; - return SourceContext == otherValue.SourceContext - && DynamicallyAccessedMemberTypes == otherValue.DynamicallyAccessedMemberTypes; - } - } - - /// - /// A value that came from a method parameter - such as the result of a ldarg. - /// - class MethodParameterValue : LeafValueWithDynamicallyAccessedMemberNode - { - public MethodParameterValue (TypeDefinition? staticType, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, IMetadataTokenProvider sourceContext) - : base (sourceContext) - { - Kind = ValueNodeKind.MethodParameter; - StaticType = staticType; - ParameterIndex = parameterIndex; - DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; - } - - public int ParameterIndex { get; } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - var otherValue = (MethodParameterValue) other; - return this.ParameterIndex == otherValue.ParameterIndex; - } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, ParameterIndex, DynamicallyAccessedMemberTypes); - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this, ParameterIndex, DynamicallyAccessedMemberTypes); - } - } - - /// - /// String with a known annotation. - /// - class AnnotatedStringValue : LeafValueWithDynamicallyAccessedMemberNode - { - public AnnotatedStringValue (IMetadataTokenProvider sourceContext, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - : base (sourceContext) - { - Kind = ValueNodeKind.AnnotatedString; - - // Should be System.String, but we don't have a use case for it - StaticType = null; - - DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; - } - - public override bool Equals (ValueNode? other) - { - return base.Equals (other); - } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, DynamicallyAccessedMemberTypes); - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this, DynamicallyAccessedMemberTypes); - } - } - - /// - /// Return value from a method - /// - class MethodReturnValue : LeafValueWithDynamicallyAccessedMemberNode - { - public MethodReturnValue (TypeDefinition? staticType, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes, IMetadataTokenProvider sourceContext) - : base (sourceContext) - { - Kind = ValueNodeKind.MethodReturn; - StaticType = staticType; - DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; - } - - public override bool Equals (ValueNode? other) - { - return base.Equals (other); - } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, DynamicallyAccessedMemberTypes); - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this, DynamicallyAccessedMemberTypes); - } - } - - /// - /// A merge point commonly occurs due to control flow in a method body. It represents a set of values - /// from different paths through the method. It is the reason for EvaluateUniqueValues, which essentially - /// provides an enumeration over all the concrete values represented by a given ValueNode after 'erasing' - /// the merge point nodes. - /// - class MergePointValue : ValueNode - { - private MergePointValue (ValueNode one, ValueNode two) - { - Kind = ValueNodeKind.MergePoint; - StaticType = null; - m_values = new ValueNodeHashSet (); - - if (one.Kind == ValueNodeKind.MergePoint) { - MergePointValue mpvOne = (MergePointValue) one; - foreach (ValueNode value in mpvOne.Values) - m_values.Add (value); - } else - m_values.Add (one); - - if (two.Kind == ValueNodeKind.MergePoint) { - MergePointValue mpvTwo = (MergePointValue) two; - foreach (ValueNode value in mpvTwo.Values) - m_values.Add (value); - } else - m_values.Add (two); - } - - public MergePointValue () - { - Kind = ValueNodeKind.MergePoint; - m_values = new ValueNodeHashSet (); - } - - public void AddValue (ValueNode node) - { - // we are mutating our state, so we must invalidate any cached knowledge - //InvalidateIsOpen (); - - if (node.Kind == ValueNodeKind.MergePoint) { - foreach (ValueNode value in ((MergePointValue) node).Values) - m_values.Add (value); - } else - m_values.Add (node); - -#if false - if (this.DetectCycle(new HashSet())) - { - throw new Exception("Found a cycle"); - } -#endif - } - - readonly ValueNodeHashSet m_values; - - public ValueNodeHashSet Values { get { return m_values; } } - - protected override int NumChildren { get { return Values.Count; } } - protected override ValueNode ChildAt (int index) - { - if (index < NumChildren) - return Values.ElementAt (index); - throw new InvalidOperationException (); - } - - public static ValueNode? MergeValues (ValueNode? one, ValueNode? two) - { - if (one == null) - return two; - else if (two == null) - return one; - else if (one.Equals (two)) - return one; - else - return new MergePointValue (one, two); - } - - protected override IEnumerable EvaluateUniqueValues () - { - foreach (ValueNode value in Values) { - foreach (ValueNode uniqueValue in value.UniqueValuesInternal) { - yield return uniqueValue; - } - } - } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - MergePointValue otherMpv = (MergePointValue) other; - if (this.Values.Count != otherMpv.Values.Count) - return false; - - foreach (ValueNode value in this.Values) { - if (!otherMpv.Values.Contains (value)) - return false; - } - return true; - } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, Values); - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this); - } - } - - delegate TypeDefinition TypeResolver (string assemblyString, string typeString); - - /// - /// The result of a Type.GetType. - /// AssemblyIdentity is the scope in which to resolve if the type name string is not assembly-qualified. - /// - -#pragma warning disable CA1812 // GetTypeFromStringValue is never instantiated - class GetTypeFromStringValue : ValueNode - { - private readonly TypeResolver _resolver; - - public GetTypeFromStringValue (TypeResolver resolver, ValueNode assemblyIdentity, ValueNode nameString) - { - _resolver = resolver; - Kind = ValueNodeKind.GetTypeFromString; - - // Should be System.Type, but we don't have a use case for it - StaticType = null; - - AssemblyIdentity = assemblyIdentity; - NameString = nameString; - } - - public ValueNode AssemblyIdentity { get; private set; } - - public ValueNode NameString { get; private set; } - - protected override int NumChildren { get { return 2; } } - protected override ValueNode ChildAt (int index) - { - if (index == 0) return AssemblyIdentity; - if (index == 1) return NameString; - throw new InvalidOperationException (); - } - - protected override IEnumerable EvaluateUniqueValues () - { - HashSet? names = null; - - foreach (ValueNode nameStringValue in NameString.UniqueValuesInternal) { - if (nameStringValue.Kind == ValueNodeKind.KnownString) { - if (names == null) { - names = new HashSet (); - } - - string typeName = ((KnownStringValue) nameStringValue).Contents; - names.Add (typeName); - } - } - - bool foundAtLeastOne = false; - - if (names != null) { - foreach (ValueNode assemblyValue in AssemblyIdentity.UniqueValuesInternal) { - if (assemblyValue.Kind == ValueNodeKind.KnownString) { - string assemblyName = ((KnownStringValue) assemblyValue).Contents; - - foreach (string name in names) { - TypeDefinition typeDefinition = _resolver (assemblyName, name); - if (typeDefinition != null) { - foundAtLeastOne = true; - yield return new SystemTypeValue (typeDefinition); - } - } - } - } - } - - if (!foundAtLeastOne) - yield return UnknownValue.Instance; - } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - GetTypeFromStringValue otherGtfs = (GetTypeFromStringValue) other; - - return this.AssemblyIdentity.Equals (otherGtfs.AssemblyIdentity) && - this.NameString.Equals (otherGtfs.NameString); - } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, AssemblyIdentity, NameString); - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this, NameString); - } - } - - /// - /// A representation of a ldfld. Note that we don't have a representation of objects containing fields - /// so there isn't much that can be done with this node type yet. - /// - class LoadFieldValue : LeafValueWithDynamicallyAccessedMemberNode - { - public LoadFieldValue (TypeDefinition? staticType, FieldDefinition fieldToLoad, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - : base (fieldToLoad) - { - Kind = ValueNodeKind.LoadField; - StaticType = staticType; - Field = fieldToLoad; - DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; - } - - public FieldDefinition Field { get; private set; } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - LoadFieldValue otherLfv = (LoadFieldValue) other; - return Equals (this.Field, otherLfv.Field); - } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, Field, DynamicallyAccessedMemberTypes); - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this, Field, DynamicallyAccessedMemberTypes); - } - } - - /// - /// Represents a ldc on an int32. - /// - class ConstIntValue : LeafValueNode - { - public ConstIntValue (int value) - { - Kind = ValueNodeKind.ConstInt; - - // Should be System.Int32, but we don't have a usecase for it right now - StaticType = null; - - Value = value; - } - - public int Value { get; private set; } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, Value); - } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - ConstIntValue otherCiv = (ConstIntValue) other; - return Value == otherCiv.Value; - } - - protected override string NodeToString () - { - return ValueNodeDump.ValueNodeToString (this, Value); - } - } - - class ArrayValue : ValueNode - { - protected override int NumChildren => 1 + IndexValues.Count; - - /// - /// Constructs an array value of the given size - /// - public ArrayValue (ValueNode? size, TypeReference elementType) - { - Kind = ValueNodeKind.Array; - - // Should be System.Array (or similar), but we don't have a use case for it - StaticType = null; - - Size = size ?? UnknownValue.Instance; - ElementType = elementType; - IndexValues = new Dictionary (); - } - - private ArrayValue (ValueNode size, TypeReference elementType, Dictionary indexValues) - : this (size, elementType) - { - IndexValues = indexValues; - } - - public ValueNode Size { get; } - public TypeReference ElementType { get; } - public Dictionary IndexValues { get; } - - public override int GetHashCode () - { - return HashCode.Combine (Kind, Size); - } - - public override bool Equals (ValueNode? other) - { - if (!base.Equals (other)) - return false; - - ArrayValue otherArr = (ArrayValue) other; - bool equals = Size.Equals (otherArr.Size); - equals &= IndexValues.Count == otherArr.IndexValues.Count; - if (!equals) - return false; - - // If both sets T and O are the same size and "T intersect O" is empty, then T == O. - HashSet> thisValueSet = new (IndexValues); - HashSet> otherValueSet = new (otherArr.IndexValues); - thisValueSet.ExceptWith (otherValueSet); - return thisValueSet.Count == 0; - } - - protected override string NodeToString () - { - // TODO: Use StringBuilder and remove Linq usage. - return $"(Array Size:{ValueNodeDump.ValueNodeToString (this, Size)}, Values:({string.Join (',', IndexValues.Select (v => $"({v.Key},{ValueNodeDump.ValueNodeToString (v.Value.Value)})"))})"; - } - - protected override IEnumerable EvaluateUniqueValues () - { - foreach (var sizeConst in Size.UniqueValuesInternal) - yield return new ArrayValue (sizeConst, ElementType, IndexValues); - } - - protected override ValueNode ChildAt (int index) - { - if (index == 0) return Size; - if (index - 1 <= IndexValues.Count) - return IndexValues.Values.ElementAt (index - 1).Value!; - - throw new InvalidOperationException (); - } - } - - #region ValueNode Collections - public class ValueNodeList : List + public class ValueNodeList : List { public ValueNodeList () { @@ -1300,14 +18,17 @@ public ValueNodeList (int capacity) { } - public ValueNodeList (List other) + public ValueNodeList (List other) : base (other) { } public override int GetHashCode () { - return HashUtils.CalcHashCodeEnumerable (this); + HashCode hashCode = new HashCode (); + foreach (var item in this) + hashCode.Add (item.GetHashCode ()); + return hashCode.ToHashCode (); } public override bool Equals (object? other) @@ -1319,56 +40,22 @@ public override bool Equals (object? other) return false; for (int i = 0; i < Count; i++) { - if (!(otherList[i]?.Equals (this[i]) ?? (this[i] is null))) - return false; - } - return true; - } - } - - class ValueNodeHashSet : HashSet - { - public override int GetHashCode () - { - return HashUtils.CalcHashCodeEnumerable (this); - } - - public override bool Equals (object? other) - { - if (!(other is ValueNodeHashSet otherSet)) - return false; - - if (otherSet.Count != Count) - return false; - - IEnumerator thisEnumerator = this.GetEnumerator (); - IEnumerator otherEnumerator = otherSet.GetEnumerator (); - - for (int i = 0; i < Count; i++) { - thisEnumerator.MoveNext (); - otherEnumerator.MoveNext (); - if (!thisEnumerator.Current.Equals (otherEnumerator.Current)) + if (!otherList[i].Equals (this[i])) return false; } return true; } } - #endregion - static class HashUtils + public struct ValueBasicBlockPair { - public static int CalcHashCodeEnumerable (IEnumerable list) where T : class? + public ValueBasicBlockPair (MultiValue value, int basicBlockIndex) { - HashCode hashCode = new HashCode (); - foreach (var item in list) - hashCode.Add (item); - return hashCode.ToHashCode (); + Value = value; + BasicBlockIndex = basicBlockIndex; } - } - public struct ValueBasicBlockPair - { - public ValueNode? Value; - public int BasicBlockIndex; + public MultiValue Value { get; } + public int BasicBlockIndex { get; } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs new file mode 100644 index 0000000000000..3b19dbfcf1ad2 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs @@ -0,0 +1,197 @@ +// 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; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using ILCompiler.DependencyAnalysis; +using ILCompiler.Logging; +using ILLink.Shared; +using ILLink.Shared.TrimAnalysis; +using Internal.TypeSystem; + +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; + +#nullable enable + +namespace ILCompiler.Dataflow +{ + internal class ReflectionMarker + { + private DependencyList _dependencies = new DependencyList(); + private readonly Logger _logger; + private readonly NodeFactory _factory; + private readonly FlowAnnotations _annotations; + private bool _typeHierarchyDataFlow; + private const string RequiresUnreferencedCodeAttribute = nameof(RequiresUnreferencedCodeAttribute); + + public DependencyList Dependencies { get => _dependencies; } + + public ReflectionMarker(Logger logger, NodeFactory factory, FlowAnnotations annotations, bool typeHierarchyDataFlow) + { + _logger = logger; + _factory = factory; + _annotations = annotations; + _typeHierarchyDataFlow = typeHierarchyDataFlow; + } + + internal void MarkTypeForDynamicallyAccessedMembers(in MessageOrigin origin, TypeDesc typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes, Origin memberWithRequirements, bool declaredOnly = false) + { + foreach (var member in typeDefinition.GetDynamicallyAccessedMembers(requiredMemberTypes, declaredOnly)) + { + switch (member) + { + case MethodDesc method: + MarkMethod(origin, method, memberWithRequirements); + break; + case FieldDesc field: + MarkField(origin, field, memberWithRequirements); + break; + case MetadataType nestedType: + MarkType(origin, nestedType, memberWithRequirements); + break; + case PropertyPseudoDesc property: + MarkProperty(origin, property, memberWithRequirements); + break; + case EventPseudoDesc @event: + MarkEvent(origin, @event, memberWithRequirements); + break; + // case InterfaceImplementation + // Nothing to do currently as Native AOT will presere all interfaces on a preserved type + } + } + } + + internal bool TryResolveTypeNameAndMark(string typeName, MessageOrigin origin, bool needsAssemblyName, Origin memberWithRequirements, [NotNullWhen(true)] out TypeDesc? type) + { + ModuleDesc? callingModule = ((origin.MemberDefinition as MethodDesc)?.OwningType as MetadataType)?.Module; + + if (!ILCompiler.DependencyAnalysis.ReflectionMethodBodyScanner.ResolveType(typeName, callingModule, origin.MemberDefinition.Context, out TypeDesc foundType, out ModuleDesc referenceModule)) + { + type = default; + return false; + } + + // Also add module metadata in case this reference was through a type forward + if (_factory.MetadataManager.CanGenerateMetadata(referenceModule.GetGlobalModuleType())) + _dependencies.Add(_factory.ModuleMetadata(referenceModule), memberWithRequirements.ToString()); + + MarkType(origin, foundType, memberWithRequirements); + + type = foundType; + return true; + } + + internal void MarkType(in MessageOrigin origin, TypeDesc type, Origin memberWithRequirements) + { + RootingHelpers.TryGetDependenciesForReflectedType(ref _dependencies, _factory, type, memberWithRequirements.ToString()); + } + + internal void MarkMethod(in MessageOrigin origin, MethodDesc method, Origin memberWithRequirements) + { + if (method.DoesMethodRequire(RequiresUnreferencedCodeAttribute, out _)) + { + if (_typeHierarchyDataFlow) + { + _logger.LogWarning(origin, DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesMemberOnBaseWithRequiresUnreferencedCode, + ((TypeOrigin)memberWithRequirements).GetDisplayName(), method.GetDisplayName()); + } + } + + if (_annotations.ShouldWarnWhenAccessedForReflection(method) && !ReflectionMethodBodyScanner.ShouldSuppressAnalysisWarningsForRequires(method, RequiresUnreferencedCodeAttribute)) + { + WarnOnReflectionAccess(origin, method, memberWithRequirements); + } + + RootingHelpers.TryGetDependenciesForReflectedMethod(ref _dependencies, _factory, method, memberWithRequirements.ToString()); + } + + void MarkField(in MessageOrigin origin, FieldDesc field, Origin memberWithRequirements) + { + if (_annotations.ShouldWarnWhenAccessedForReflection(field) && !ReflectionMethodBodyScanner.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, RequiresUnreferencedCodeAttribute)) + { + WarnOnReflectionAccess(origin, field, memberWithRequirements); + } + + RootingHelpers.TryGetDependenciesForReflectedField(ref _dependencies, _factory, field, memberWithRequirements.ToString()); + } + + internal void MarkProperty(in MessageOrigin origin, PropertyPseudoDesc property, Origin memberWithRequirements) + { + if (property.GetMethod != null) + MarkMethod(origin, property.GetMethod, memberWithRequirements); + if (property.SetMethod != null) + MarkMethod(origin, property.SetMethod, memberWithRequirements); + } + + void MarkEvent(in MessageOrigin origin, EventPseudoDesc @event, Origin memberWithRequirements) + { + if (@event.AddMethod != null) + MarkMethod(origin, @event.AddMethod, memberWithRequirements); + if (@event.RemoveMethod != null) + MarkMethod(origin, @event.RemoveMethod, memberWithRequirements); + } + + internal void MarkConstructorsOnType(in MessageOrigin origin, TypeDesc type, Func? filter, Origin memberWithRequirements, BindingFlags? bindingFlags = null) + { + foreach (var ctor in type.GetConstructorsOnType(filter, bindingFlags)) + MarkMethod(origin, ctor, memberWithRequirements); + } + + internal void MarkFieldsOnTypeHierarchy(in MessageOrigin origin, TypeDesc type, Func filter, Origin memberWithRequirements, BindingFlags? bindingFlags = BindingFlags.Default) + { + foreach (var field in type.GetFieldsOnTypeHierarchy(filter, bindingFlags)) + MarkField(origin, field, memberWithRequirements); + } + + internal void MarkPropertiesOnTypeHierarchy(in MessageOrigin origin, TypeDesc type, Func filter, Origin memberWithRequirements, BindingFlags? bindingFlags = BindingFlags.Default) + { + foreach (var property in type.GetPropertiesOnTypeHierarchy(filter, bindingFlags)) + MarkProperty(origin, property, memberWithRequirements); + } + + internal void MarkEventsOnTypeHierarchy(in MessageOrigin origin, TypeDesc type, Func filter, Origin memberWithRequirements, BindingFlags? bindingFlags = BindingFlags.Default) + { + foreach (var @event in type.GetEventsOnTypeHierarchy(filter, bindingFlags)) + MarkEvent(origin, @event, memberWithRequirements); + } + + internal void MarkStaticConstructor(in MessageOrigin origin, TypeDesc type) + { + if (!type.IsGenericDefinition && !type.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true) && type.HasStaticConstructor) + { + _dependencies.Add(_factory.CanonicalEntrypoint(type.GetStaticConstructor()), "RunClassConstructor reference"); + } + } + + void WarnOnReflectionAccess(in MessageOrigin origin, TypeSystemEntity entity, Origin memberWithRequirements) + { + if (_typeHierarchyDataFlow) + { + // Don't check whether the current scope is a RUC type or RUC method because these warnings + // are not suppressed in RUC scopes. Here the scope represents the DynamicallyAccessedMembers + // annotation on a type, not a callsite which uses the annotation. We always want to warn about + // possible reflection access indicated by these annotations. + _logger.LogWarning(origin, DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesMemberOnBaseWithDynamicallyAccessedMembers, + ((TypeOrigin)memberWithRequirements).GetDisplayName(), entity.GetDisplayName()); + } + else + { + if (!ReflectionMethodBodyScanner.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, RequiresUnreferencedCodeAttribute)) + { + if (entity is FieldDesc) + { + _logger.LogWarning(origin, DiagnosticId.DynamicallyAccessedMembersFieldAccessedViaReflection, entity.GetDisplayName()); + } + else + { + Debug.Assert(entity is MethodDesc); + + _logger.LogWarning(origin, DiagnosticId.DynamicallyAccessedMembersMethodAccessedViaReflection, entity.GetDisplayName()); + } + } + } + } + } +} 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 c754c50ed99cf..f584f7c5b2023 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -1,44 +1,46 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection.Metadata; -using ILLink.Shared; - using ILCompiler.Logging; +using ILLink.Shared; +using ILLink.Shared.DataFlow; +using ILLink.Shared.TrimAnalysis; using Internal.IL; using Internal.TypeSystem; -using BindingFlags = System.Reflection.BindingFlags; -using NodeFactory = ILCompiler.DependencyAnalysis.NodeFactory; -using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; -using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue; -using CustomAttributeTypedArgument = System.Reflection.Metadata.CustomAttributeTypedArgument; using CustomAttributeNamedArgumentKind = System.Reflection.Metadata.CustomAttributeNamedArgumentKind; +using CustomAttributeValue = System.Reflection.Metadata.CustomAttributeValue; +using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; using InteropTypes = Internal.TypeSystem.Interop.InteropTypes; +using MultiValue = ILLink.Shared.DataFlow.ValueSet; +using NodeFactory = ILCompiler.DependencyAnalysis.NodeFactory; +using WellKnownType = ILLink.Shared.TypeSystemProxy.WellKnownType; + +#nullable enable namespace ILCompiler.Dataflow { class ReflectionMethodBodyScanner : MethodBodyScanner { - private readonly FlowAnnotations _flowAnnotations; + private readonly FlowAnnotations _annotations; private readonly Logger _logger; private readonly NodeFactory _factory; - private DependencyList _dependencies = new DependencyList(); + private readonly ReflectionMarker _reflectionMarker; private const string RequiresUnreferencedCodeAttribute = nameof(RequiresUnreferencedCodeAttribute); private const string RequiresDynamicCodeAttribute = nameof(RequiresDynamicCodeAttribute); private const string RequiresAssemblyFilesAttribute = nameof(RequiresAssemblyFilesAttribute); public static bool RequiresReflectionMethodBodyScannerForCallSite(FlowAnnotations flowAnnotations, MethodDesc methodDefinition) { - return - GetIntrinsicIdForMethod(methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || + return Intrinsics.GetIntrinsicIdForMethod(methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || flowAnnotations.RequiresDataflowAnalysis(methodDefinition) || methodDefinition.DoesMethodRequire(RequiresUnreferencedCodeAttribute, out _) || methodDefinition.DoesMethodRequire(RequiresDynamicCodeAttribute, out _) || @@ -47,8 +49,7 @@ public static bool RequiresReflectionMethodBodyScannerForCallSite(FlowAnnotation public static bool RequiresReflectionMethodBodyScannerForMethodBody(FlowAnnotations flowAnnotations, MethodDesc methodDefinition) { - return - GetIntrinsicIdForMethod(methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || + return Intrinsics.GetIntrinsicIdForMethod(methodDefinition) > IntrinsicId.RequiresReflectionBodyScanner_Sentinel || flowAnnotations.RequiresDataflowAnalysis(methodDefinition); } @@ -77,10 +78,10 @@ void CheckAndReportRequires(TypeSystemEntity calledMember, in MessageOrigin orig _ => throw new NotImplementedException($"{requiresAttributeName} is not a valid supported Requires attribute"), }; - ReportRequires(calledMember.GetDisplayName(), origin, diagnosticId, requiresAttribute); + ReportRequires(calledMember.GetDisplayName(), origin, diagnosticId, requiresAttribute.Value); } - static bool ShouldSuppressAnalysisWarningsForRequires(TypeSystemEntity originMember, string requiresAttribute) + internal static bool ShouldSuppressAnalysisWarningsForRequires(TypeSystemEntity originMember, string requiresAttribute) { // Check if the current scope method has Requires on it // since that attribute automatically suppresses all trim analysis warnings. @@ -103,7 +104,7 @@ static bool ShouldSuppressAnalysisWarningsForRequires(TypeSystemEntity originMem return false; } - void ReportRequires(string displayName, in MessageOrigin currentOrigin, DiagnosticId diagnosticId, CustomAttributeValue? requiresAttribute) + void ReportRequires(string displayName, in MessageOrigin currentOrigin, DiagnosticId diagnosticId, CustomAttributeValue requiresAttribute) { string arg1 = MessageFormat.FormatRequiresAttributeMessageArg(DiagnosticUtilities.GetRequiresAttributeMessage((CustomAttributeValue)requiresAttribute)); string arg2 = MessageFormat.FormatRequiresAttributeUrlArg(DiagnosticUtilities.GetRequiresAttributeUrl((CustomAttributeValue)requiresAttribute)); @@ -119,17 +120,18 @@ private enum ScanningPurpose private ScanningPurpose _purpose; - private ReflectionMethodBodyScanner(NodeFactory factory, FlowAnnotations flowAnnotations, Logger logger, ScanningPurpose purpose = ScanningPurpose.Default) + private ReflectionMethodBodyScanner(NodeFactory factory, FlowAnnotations annotations, Logger logger, ScanningPurpose purpose = ScanningPurpose.Default) { - _flowAnnotations = flowAnnotations; + _annotations = annotations; _logger = logger; _factory = factory; _purpose = purpose; + _reflectionMarker = new ReflectionMarker(logger, factory, annotations, purpose == ScanningPurpose.GetTypeDataflow); } - public static DependencyList ScanAndProcessReturnValue(NodeFactory factory, FlowAnnotations flowAnnotations, Logger logger, MethodIL methodBody) + public static DependencyList ScanAndProcessReturnValue(NodeFactory factory, FlowAnnotations annotations, Logger logger, MethodIL methodBody) { - var scanner = new ReflectionMethodBodyScanner(factory, flowAnnotations, logger); + var scanner = new ReflectionMethodBodyScanner(factory, annotations, logger); Debug.Assert(methodBody.GetMethodILDefinition() == methodBody); if (methodBody.OwningMethod.HasInstantiation || methodBody.OwningMethod.OwningType.HasInstantiation) @@ -156,53 +158,34 @@ public static DependencyList ScanAndProcessReturnValue(NodeFactory factory, Flow if (!methodBody.OwningMethod.Signature.ReturnType.IsVoid) { var method = methodBody.OwningMethod; - var requiredMemberTypes = scanner._flowAnnotations.GetReturnParameterAnnotation(method); - if (requiredMemberTypes != 0) + var methodReturnValue = scanner._annotations.GetMethodReturnValue(method); + if (methodReturnValue.DynamicallyAccessedMemberTypes != 0) { - var targetContext = new MethodReturnOrigin(method); - bool shouldEnableReflectionWarnings = !ShouldSuppressAnalysisWarningsForRequires(method, RequiresUnreferencedCodeAttribute); - var reflectionContext = new ReflectionPatternContext(scanner._logger, - shouldEnableReflectionWarnings, - method, - targetContext); - reflectionContext.AnalyzingPattern(); - scanner.RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, scanner.MethodReturnValue, targetContext); - reflectionContext.Dispose(); + var diagnosticContext = new DiagnosticContext(new MessageOrigin(method), !ShouldSuppressAnalysisWarningsForRequires(method, RequiresUnreferencedCodeAttribute), scanner._logger); + scanner.RequireDynamicallyAccessedMembers(diagnosticContext, scanner.ReturnValue, methodReturnValue, new MethodReturnOrigin(method)); } } - return scanner._dependencies; + return scanner._reflectionMarker.Dependencies; } - public static DependencyList ProcessAttributeDataflow(NodeFactory factory, FlowAnnotations flowAnnotations, Logger logger, MethodDesc method, CustomAttributeValue arguments) + public static DependencyList? ProcessAttributeDataflow(NodeFactory factory, FlowAnnotations annotations, Logger logger, MethodDesc method, CustomAttributeValue arguments) { - DependencyList result = null; + DependencyList? result = null; // First do the dataflow for the constructor parameters if necessary. - if (flowAnnotations.RequiresDataflowAnalysis(method)) + if (annotations.RequiresDataflowAnalysis(method)) { for (int i = 0; i < method.Signature.Length; i++) { - DynamicallyAccessedMemberTypes annotation = flowAnnotations.GetParameterAnnotation(method, i + 1); - if (annotation != DynamicallyAccessedMemberTypes.None) + var parameterValue = annotations.GetMethodParameterValue(method, i); + if (parameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None) { - ValueNode valueNode = GetValueNodeForCustomAttributeArgument(arguments.FixedArguments[i].Value); - if (valueNode != null) - { - var targetContext = new ParameterOrigin(method, i); - var reflectionContext = new ReflectionPatternContext(logger, true, method, targetContext); - try - { - reflectionContext.AnalyzingPattern(); - var scanner = new ReflectionMethodBodyScanner(factory, flowAnnotations, logger); - scanner.RequireDynamicallyAccessedMembers(ref reflectionContext, annotation, valueNode, targetContext); - result = scanner._dependencies; - } - finally - { - reflectionContext.Dispose(); - } - } + MultiValue value = GetValueForCustomAttributeArgument(arguments.FixedArguments[i].Value); + var diagnosticContext = new DiagnosticContext(new MessageOrigin(method), diagnosticsEnabled: true, logger); + var scanner = new ReflectionMethodBodyScanner(factory, annotations, logger); + scanner.RequireDynamicallyAccessedMembers(diagnosticContext, value, parameterValue, parameterValue.ParameterOrigin); + AddResults(scanner._reflectionMarker.Dependencies); } } } @@ -211,16 +194,14 @@ public static DependencyList ProcessAttributeDataflow(NodeFactory factory, FlowA TypeDesc attributeType = method.OwningType; foreach (var namedArgument in arguments.NamedArguments) { - TypeSystemEntity entity = null; - DynamicallyAccessedMemberTypes annotation = DynamicallyAccessedMemberTypes.None; - Origin targetContext = null; + MultiValue targetValues = new(); + Origin? targetContext = null; if (namedArgument.Kind == CustomAttributeNamedArgumentKind.Field) { FieldDesc field = attributeType.GetField(namedArgument.Name); if (field != null) { - annotation = flowAnnotations.GetFieldAnnotation(field); - entity = field; + targetValues = GetFieldValue(field, annotations); targetContext = new FieldOrigin(field); } } @@ -231,59 +212,52 @@ public static DependencyList ProcessAttributeDataflow(NodeFactory factory, FlowA MethodDesc setter = property.SetMethod; if (setter != null && setter.Signature.Length > 0 && !setter.Signature.IsStatic) { - annotation = flowAnnotations.GetParameterAnnotation(setter, 1); - entity = property; + targetValues = annotations.GetMethodParameterValue(setter, 0); targetContext = new ParameterOrigin(setter, 1); } } - if (annotation != DynamicallyAccessedMemberTypes.None) + foreach (var targetValueCandidate in targetValues) { - ValueNode valueNode = GetValueNodeForCustomAttributeArgument(namedArgument.Value); - if (valueNode != null) - { - var reflectionContext = new ReflectionPatternContext(logger, true, method, targetContext); - try - { - reflectionContext.AnalyzingPattern(); - var scanner = new ReflectionMethodBodyScanner(factory, flowAnnotations, logger); - scanner.RequireDynamicallyAccessedMembers(ref reflectionContext, annotation, valueNode, targetContext); - if (result == null) - { - result = scanner._dependencies; - } - else - { - result.AddRange(scanner._dependencies); - } - } - finally - { - reflectionContext.Dispose(); - } - } + if (targetValueCandidate is not ValueWithDynamicallyAccessedMembers targetValue || + targetValue.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.None) + continue; + + MultiValue valueNode = GetValueForCustomAttributeArgument(namedArgument.Value); + var diagnosticContext = new DiagnosticContext(new MessageOrigin(method), diagnosticsEnabled: true, logger); + var scanner = new ReflectionMethodBodyScanner(factory, annotations, logger); + scanner.RequireDynamicallyAccessedMembers(diagnosticContext, valueNode, targetValue, targetContext!); + AddResults(scanner._reflectionMarker.Dependencies); } } return result; + + void AddResults(DependencyList dependencies) + { + if (result == null) + { + result = dependencies; + } + else + { + result.AddRange(dependencies); + } + } } public static DependencyList ProcessTypeGetTypeDataflow(NodeFactory factory, FlowAnnotations flowAnnotations, Logger logger, MetadataType type) { DynamicallyAccessedMemberTypes annotation = flowAnnotations.GetTypeAnnotation(type); Debug.Assert(annotation != DynamicallyAccessedMemberTypes.None); - var scanner = new ReflectionMethodBodyScanner(factory, flowAnnotations, logger, ScanningPurpose.GetTypeDataflow); - ReflectionPatternContext reflectionPatternContext = new ReflectionPatternContext(logger, reportingEnabled: true, type, new TypeOrigin(type)); - reflectionPatternContext.AnalyzingPattern(); - scanner.MarkTypeForDynamicallyAccessedMembers(ref reflectionPatternContext, type, annotation); - reflectionPatternContext.RecordHandledPattern(); - reflectionPatternContext.Dispose(); - return scanner._dependencies; + var reflectionMarker = new ReflectionMarker(logger, factory, flowAnnotations, true); + reflectionMarker.MarkTypeForDynamicallyAccessedMembers(new MessageOrigin(type), type, annotation, new TypeOrigin(type)); + return reflectionMarker.Dependencies; } - static ValueNode GetValueNodeForCustomAttributeArgument(object argument) + static MultiValue GetValueForCustomAttributeArgument(object? argument) { - ValueNode result = null; + SingleValue? result = null; if (argument is TypeDesc td) { result = new SystemTypeValue(td); @@ -298,6 +272,7 @@ static ValueNode GetValueNodeForCustomAttributeArgument(object argument) result = NullValue.Instance; } + Debug.Assert(result != null); return result; } @@ -305,18 +280,47 @@ public static DependencyList ProcessGenericArgumentDataFlow(NodeFactory factory, { var scanner = new ReflectionMethodBodyScanner(factory, flowAnnotations, logger); - var annotation = flowAnnotations.GetGenericParameterAnnotation(genericParameter); - Debug.Assert(annotation != DynamicallyAccessedMemberTypes.None); + var genericParameterValue = flowAnnotations.GetGenericParameterValue(genericParameter); + Debug.Assert(genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None); - ValueNode valueNode = new SystemTypeValue(genericArgument); + MultiValue genericArgumentValue = scanner.GetTypeValueNodeFromGenericArgument(genericArgument); + bool enableDiagnostics = ShouldSuppressAnalysisWarningsForRequires(source, RequiresUnreferencedCodeAttribute); + var diagnosticContext = new DiagnosticContext(new MessageOrigin(source), diagnosticsEnabled: enableDiagnostics, logger); var origin = new GenericParameterOrigin(genericParameter); - var reflectionContext = new ReflectionPatternContext(logger, reportingEnabled: true, source, origin); - reflectionContext.AnalyzingPattern(); - scanner.RequireDynamicallyAccessedMembers(ref reflectionContext, annotation, valueNode, origin); - reflectionContext.Dispose(); + scanner.RequireDynamicallyAccessedMembers(diagnosticContext, genericArgumentValue, genericParameterValue, origin); + + return scanner._reflectionMarker.Dependencies; + } - return scanner._dependencies; + MultiValue GetTypeValueNodeFromGenericArgument(TypeDesc genericArgument) + { + if (genericArgument is GenericParameterDesc inputGenericParameter) + { + return _annotations.GetGenericParameterValue(inputGenericParameter); + } + else if (genericArgument is MetadataType genericArgumentType) + { + if (genericArgumentType.IsTypeOf(WellKnownType.System_Nullable_T)) + { + var innerGenericArgument = genericArgumentType.Instantiation.Length == 1 ? genericArgumentType.Instantiation[0] : null; + switch (innerGenericArgument) + { + case GenericParameterDesc gp: + return new NullableValueWithDynamicallyAccessedMembers(genericArgumentType, + new GenericParameterValue(gp, _annotations.GetGenericParameterAnnotation(gp))); + + case TypeDesc underlyingType: + return new NullableSystemTypeValue(genericArgumentType, new SystemTypeValue(underlyingType)); + } + } + // All values except for Nullable, including Nullable<> (with no type arguments) + return new SystemTypeValue(genericArgumentType); + } + else + { + return UnknownValue.Instance; + } } protected override void WarnAboutInvalidILInMethod(MethodIL method, int ilOffset) @@ -330,1972 +334,389 @@ protected override void WarnAboutInvalidILInMethod(MethodIL method, int ilOffset Debug.Fail("Invalid IL or a bug in the scanner"); } - protected override ValueNode GetMethodParameterValue(MethodDesc method, int parameterIndex) + protected override ValueWithDynamicallyAccessedMembers GetMethodParameterValue(MethodDesc method, int parameterIndex) + => GetMethodParameterValue(method, parameterIndex, _annotations.GetParameterAnnotation(method, parameterIndex)); + + ValueWithDynamicallyAccessedMembers GetMethodParameterValue(MethodDesc method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) { - DynamicallyAccessedMemberTypes memberTypes = _flowAnnotations.GetParameterAnnotation(method, parameterIndex); - return new MethodParameterValue(method, parameterIndex, memberTypes); + if (!method.Signature.IsStatic) + { + if (parameterIndex == 0) + return _annotations.GetMethodThisParameterValue(method, dynamicallyAccessedMemberTypes); + + parameterIndex--; + } + + return _annotations.GetMethodParameterValue(method, parameterIndex, dynamicallyAccessedMemberTypes); } - protected override ValueNode GetFieldValue(MethodIL method, FieldDesc field) + static MultiValue GetFieldValue(FieldDesc field, FlowAnnotations annotations) { switch (field.Name) { - case "EmptyTypes" when field.OwningType.IsTypeOf("System", "Type"): + case "EmptyTypes" when field.OwningType.IsTypeOf(ILLink.Shared.TypeSystemProxy.WellKnownType.System_Type): { - return new ArrayValue(new ConstIntValue(0), field.OwningType); + return ArrayValue.Create(0, field.OwningType); } - case "Empty" when field.OwningType.IsTypeOf("System", "String"): + case "Empty" when field.OwningType.IsTypeOf(ILLink.Shared.TypeSystemProxy.WellKnownType.System_String): { return new KnownStringValue(string.Empty); } default: { - DynamicallyAccessedMemberTypes memberTypes = _flowAnnotations.GetFieldAnnotation(field); - return new LoadFieldValue(field, memberTypes); + DynamicallyAccessedMemberTypes memberTypes = annotations.GetFieldAnnotation(field); + return new FieldValue(field, memberTypes); } } } - protected override void HandleStoreField(MethodIL methodBody, int offset, FieldDesc field, ValueNode valueToStore) + protected override MultiValue GetFieldValue(FieldDesc field) { - var requiredMemberTypes = _flowAnnotations.GetFieldAnnotation(field); - if (requiredMemberTypes != 0) - { - var origin = new FieldOrigin(field); - bool shouldEnableReflectionWarnings = !ShouldSuppressAnalysisWarningsForRequires(methodBody.OwningMethod, RequiresUnreferencedCodeAttribute); - var reflectionContext = new ReflectionPatternContext(_logger, shouldEnableReflectionWarnings, methodBody, offset, origin); - reflectionContext.AnalyzingPattern(); - RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, valueToStore, origin); - reflectionContext.Dispose(); - } - CheckAndReportRequires(field, new MessageOrigin(methodBody.OwningMethod), RequiresUnreferencedCodeAttribute); - CheckAndReportRequires(field, new MessageOrigin(methodBody.OwningMethod), RequiresDynamicCodeAttribute); + return GetFieldValue(field, _annotations); } - protected override void HandleStoreParameter(MethodIL method, int offset, int index, ValueNode valueToStore) + protected override void HandleStoreField(MethodIL methodBody, int offset, FieldValue field, MultiValue valueToStore) { - var requiredMemberTypes = _flowAnnotations.GetParameterAnnotation(method.OwningMethod, index); - if (requiredMemberTypes != 0) + if (field.DynamicallyAccessedMemberTypes != 0) { - Origin parameter = DiagnosticUtilities.GetMethodParameterFromIndex(method.OwningMethod, index); - bool shouldEnableReflectionWarnings = !ShouldSuppressAnalysisWarningsForRequires(method.OwningMethod, RequiresUnreferencedCodeAttribute); - var reflectionContext = new ReflectionPatternContext(_logger, shouldEnableReflectionWarnings, method, offset, parameter); - reflectionContext.AnalyzingPattern(); - RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, valueToStore, parameter); - reflectionContext.Dispose(); + var diagnosticContext = new DiagnosticContext(new MessageOrigin(methodBody, offset), !ShouldSuppressAnalysisWarningsForRequires(methodBody.OwningMethod, RequiresUnreferencedCodeAttribute), _logger); + RequireDynamicallyAccessedMembers(diagnosticContext, valueToStore, field, new FieldOrigin(field.Field)); } - } - enum IntrinsicId - { - None = 0, - IntrospectionExtensions_GetTypeInfo, - Type_GetTypeFromHandle, - Type_get_TypeHandle, - Object_GetType, - TypeDelegator_Ctor, - Array_Empty, - TypeInfo_AsType, - MethodBase_GetMethodFromHandle, - - // Anything above this marker will require the method to be run through - // the reflection body scanner. - RequiresReflectionBodyScanner_Sentinel = 1000, - Type_MakeGenericType, - Type_GetType, - Type_GetConstructor, - Type_GetConstructors, - Type_GetMethod, - Type_GetMethods, - Type_GetField, - Type_GetFields, - Type_GetProperty, - Type_GetProperties, - Type_GetEvent, - Type_GetEvents, - Type_GetNestedType, - Type_GetNestedTypes, - Type_GetMember, - Type_GetMembers, - Type_GetInterface, - Type_get_AssemblyQualifiedName, - Type_get_UnderlyingSystemType, - Type_get_BaseType, - Expression_Call, - Expression_Field, - Expression_Property, - Expression_New, - Enum_GetValues, - Marshal_SizeOf, - Marshal_OffsetOf, - Marshal_PtrToStructure, - Marshal_DestroyStructure, - Marshal_GetDelegateForFunctionPointer, - Activator_CreateInstance_Type, - Activator_CreateInstance_AssemblyName_TypeName, - Activator_CreateInstanceFrom, - Activator_CreateInstanceOfT, - AppDomain_CreateInstance, - AppDomain_CreateInstanceAndUnwrap, - AppDomain_CreateInstanceFrom, - AppDomain_CreateInstanceFromAndUnwrap, - Assembly_CreateInstance, - RuntimeReflectionExtensions_GetRuntimeEvent, - RuntimeReflectionExtensions_GetRuntimeField, - RuntimeReflectionExtensions_GetRuntimeMethod, - RuntimeReflectionExtensions_GetRuntimeProperty, - RuntimeHelpers_RunClassConstructor, - MethodInfo_MakeGenericMethod, + CheckAndReportRequires(field.Field, new MessageOrigin(methodBody.OwningMethod), RequiresUnreferencedCodeAttribute); + CheckAndReportRequires(field.Field, new MessageOrigin(methodBody.OwningMethod), RequiresDynamicCodeAttribute); } - static IntrinsicId GetIntrinsicIdForMethod(MethodDesc calledMethod) + protected override void HandleStoreParameter(MethodIL method, int offset, MethodParameterValue parameter, MultiValue valueToStore) { - return calledMethod.Name switch + if (parameter.DynamicallyAccessedMemberTypes != 0) { - // static System.Reflection.IntrospectionExtensions.GetTypeInfo (Type type) - "GetTypeInfo" when calledMethod.IsDeclaredOnType("System.Reflection", "IntrospectionExtensions") => IntrinsicId.IntrospectionExtensions_GetTypeInfo, - - // System.Reflection.TypeInfo.AsType () - "AsType" when calledMethod.IsDeclaredOnType("System.Reflection", "TypeInfo") => IntrinsicId.TypeInfo_AsType, - - // System.Type.GetTypeInfo (Type type) - "GetTypeFromHandle" when calledMethod.IsDeclaredOnType("System", "Type") => IntrinsicId.Type_GetTypeFromHandle, - - // System.Type.GetTypeHandle (Type type) - "get_TypeHandle" when calledMethod.IsDeclaredOnType("System", "Type") => IntrinsicId.Type_get_TypeHandle, - - // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle) - // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle, RuntimeTypeHandle declaringType) - "GetMethodFromHandle" when calledMethod.IsDeclaredOnType("System.Reflection", "MethodBase") - && calledMethod.HasParameterOfType(0, "System", "RuntimeMethodHandle") - && (calledMethod.Signature.Length == 1 || calledMethod.Signature.Length == 2) - => IntrinsicId.MethodBase_GetMethodFromHandle, - - // static System.Type.MakeGenericType (Type [] typeArguments) - "MakeGenericType" when calledMethod.IsDeclaredOnType("System", "Type") => IntrinsicId.Type_MakeGenericType, - - // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeEvent (this Type type, string name) - "GetRuntimeEvent" when calledMethod.IsDeclaredOnType("System.Reflection", "RuntimeReflectionExtensions") - && calledMethod.HasParameterOfType(0, "System", "Type") - && calledMethod.HasParameterOfType(1, "System", "String") - => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent, - - // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeField (this Type type, string name) - "GetRuntimeField" when calledMethod.IsDeclaredOnType("System.Reflection", "RuntimeReflectionExtensions") - && calledMethod.HasParameterOfType(0, "System", "Type") - && calledMethod.HasParameterOfType(1, "System", "String") - => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField, - - // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeMethod (this Type type, string name, Type[] parameters) - "GetRuntimeMethod" when calledMethod.IsDeclaredOnType("System.Reflection", "RuntimeReflectionExtensions") - && calledMethod.HasParameterOfType(0, "System", "Type") - && calledMethod.HasParameterOfType(1, "System", "String") - => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod, - - // static System.Reflection.RuntimeReflectionExtensions.GetRuntimeProperty (this Type type, string name) - "GetRuntimeProperty" when calledMethod.IsDeclaredOnType("System.Reflection", "RuntimeReflectionExtensions") - && calledMethod.HasParameterOfType(0, "System", "Type") - && calledMethod.HasParameterOfType(1, "System", "String") - => IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty, - - // static System.Linq.Expressions.Expression.Call (Type, String, Type[], Expression[]) - "Call" when calledMethod.IsDeclaredOnType("System.Linq.Expressions", "Expression") - && calledMethod.HasParameterOfType(0, "System", "Type") - && calledMethod.Signature.Length == 4 - => IntrinsicId.Expression_Call, - - // static System.Linq.Expressions.Expression.Field (Expression, Type, String) - "Field" when calledMethod.IsDeclaredOnType("System.Linq.Expressions", "Expression") - && calledMethod.HasParameterOfType(1, "System", "Type") - && calledMethod.Signature.Length == 3 - => IntrinsicId.Expression_Field, - - // static System.Linq.Expressions.Expression.Property (Expression, Type, String) - // static System.Linq.Expressions.Expression.Property (Expression, MethodInfo) - "Property" when calledMethod.IsDeclaredOnType("System.Linq.Expressions", "Expression") - && ((calledMethod.HasParameterOfType(1, "System", "Type") && calledMethod.Signature.Length == 3) - || (calledMethod.HasParameterOfType(1, "System.Reflection", "MethodInfo") && calledMethod.Signature.Length == 2)) - => IntrinsicId.Expression_Property, - - // static System.Linq.Expressions.Expression.New (Type) - "New" when calledMethod.IsDeclaredOnType("System.Linq.Expressions", "Expression") - && calledMethod.HasParameterOfType(0, "System", "Type") - && calledMethod.Signature.Length == 1 - => IntrinsicId.Expression_New, - - // static Array System.Enum.GetValues (Type) - "GetValues" when calledMethod.IsDeclaredOnType("System", "Enum") - && calledMethod.HasParameterOfType(0, "System", "Type") - && calledMethod.Signature.Length == 1 - => IntrinsicId.Enum_GetValues, - - // static int System.Runtime.InteropServices.Marshal.SizeOf (Type) - "SizeOf" when calledMethod.IsDeclaredOnType("System.Runtime.InteropServices", "Marshal") - && calledMethod.HasParameterOfType(0, "System", "Type") - && calledMethod.Signature.Length == 1 - => IntrinsicId.Marshal_SizeOf, - - // static int System.Runtime.InteropServices.Marshal.OffsetOf (Type, string) - "OffsetOf" when calledMethod.IsDeclaredOnType("System.Runtime.InteropServices", "Marshal") - && calledMethod.HasParameterOfType(0, "System", "Type") - && calledMethod.Signature.Length == 2 - => IntrinsicId.Marshal_OffsetOf, - - // static object System.Runtime.InteropServices.Marshal.PtrToStructure (IntPtr, Type) - "PtrToStructure" when calledMethod.IsDeclaredOnType("System.Runtime.InteropServices", "Marshal") - && calledMethod.HasParameterOfType(1, "System", "Type") - && calledMethod.Signature.Length == 2 - => IntrinsicId.Marshal_PtrToStructure, - - // static void System.Runtime.InteropServices.Marshal.DestroyStructure (IntPtr, Type) - "DestroyStructure" when calledMethod.IsDeclaredOnType("System.Runtime.InteropServices", "Marshal") - && calledMethod.HasParameterOfType(1, "System", "Type") - && calledMethod.Signature.Length == 2 - => IntrinsicId.Marshal_DestroyStructure, - - // static Delegate System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer (IntPtr, Type) - "GetDelegateForFunctionPointer" when calledMethod.IsDeclaredOnType("System.Runtime.InteropServices", "Marshal") - && calledMethod.HasParameterOfType(1, "System", "Type") - && calledMethod.Signature.Length == 2 - => IntrinsicId.Marshal_GetDelegateForFunctionPointer, - - // static System.Type.GetType (string) - // static System.Type.GetType (string, Boolean) - // static System.Type.GetType (string, Boolean, Boolean) - // static System.Type.GetType (string, Func, Func) - // static System.Type.GetType (string, Func, Func, Boolean) - // static System.Type.GetType (string, Func, Func, Boolean, Boolean) - "GetType" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System", "String") - => IntrinsicId.Type_GetType, - - // System.Type.GetConstructor (Type[]) - // System.Type.GetConstructor (BindingFlags, Type[]) - // System.Type.GetConstructor (BindingFlags, Binder, Type[], ParameterModifier []) - // System.Type.GetConstructor (BindingFlags, Binder, CallingConventions, Type[], ParameterModifier []) - "GetConstructor" when calledMethod.IsDeclaredOnType("System", "Type") - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_GetConstructor, - - // System.Type.GetConstructors (BindingFlags) - "GetConstructors" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") - && calledMethod.Signature.Length == 1 - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_GetConstructors, - - // System.Type.GetMethod (string) - // System.Type.GetMethod (string, BindingFlags) - // System.Type.GetMethod (string, Type[]) - // System.Type.GetMethod (string, Type[], ParameterModifier[]) - // System.Type.GetMethod (string, BindingFlags, Type[]) - // System.Type.GetMethod (string, BindingFlags, Binder, Type[], ParameterModifier[]) - // System.Type.GetMethod (string, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[]) - // System.Type.GetMethod (string, int, Type[]) - // System.Type.GetMethod (string, int, Type[], ParameterModifier[]?) - // System.Type.GetMethod (string, int, BindingFlags, Binder?, Type[], ParameterModifier[]?) - // System.Type.GetMethod (string, int, BindingFlags, Binder?, CallingConventions, Type[], ParameterModifier[]?) - "GetMethod" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System", "String") - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_GetMethod, - - // System.Type.GetMethods (BindingFlags) - "GetMethods" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") - && calledMethod.Signature.Length == 1 - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_GetMethods, - - // System.Type.GetField (string) - // System.Type.GetField (string, BindingFlags) - "GetField" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System", "String") - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_GetField, - - // System.Type.GetFields (BindingFlags) - "GetFields" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") - && calledMethod.Signature.Length == 1 - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_GetFields, - - // System.Type.GetEvent (string) - // System.Type.GetEvent (string, BindingFlags) - "GetEvent" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System", "String") - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_GetEvent, - - // System.Type.GetEvents (BindingFlags) - "GetEvents" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") - && calledMethod.Signature.Length == 1 - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_GetEvents, - - // System.Type.GetNestedType (string) - // System.Type.GetNestedType (string, BindingFlags) - "GetNestedType" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System", "String") - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_GetNestedType, - - // System.Type.GetNestedTypes (BindingFlags) - "GetNestedTypes" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") - && calledMethod.Signature.Length == 1 - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_GetNestedTypes, - - // System.Type.GetMember (String) - // System.Type.GetMember (String, BindingFlags) - // System.Type.GetMember (String, MemberTypes, BindingFlags) - "GetMember" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System", "String") - && !calledMethod.Signature.IsStatic - && (calledMethod.Signature.Length == 1 || - (calledMethod.Signature.Length == 2 && calledMethod.HasParameterOfType(1, "System.Reflection", "BindingFlags")) || - (calledMethod.Signature.Length == 3 && calledMethod.HasParameterOfType(2, "System.Reflection", "BindingFlags"))) - => IntrinsicId.Type_GetMember, - - // System.Type.GetMembers (BindingFlags) - "GetMembers" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") - && calledMethod.Signature.Length == 1 - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_GetMembers, - - // System.Type.GetInterface (string) - // System.Type.GetInterface (string, bool) - "GetInterface" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System", "String") - && !calledMethod.Signature.IsStatic - && (calledMethod.Signature.Length == 1 || - (calledMethod.Signature.Length == 2 && calledMethod.Signature[1].IsWellKnownType(WellKnownType.Boolean))) - => IntrinsicId.Type_GetInterface, - - // System.Type.AssemblyQualifiedName - "get_AssemblyQualifiedName" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.Signature.Length == 0 - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_get_AssemblyQualifiedName, - - // System.Type.UnderlyingSystemType - "get_UnderlyingSystemType" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.Signature.Length == 0 - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_get_UnderlyingSystemType, - - // System.Type.BaseType - "get_BaseType" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.Signature.Length == 0 - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_get_BaseType, - - // System.Type.GetProperty (string) - // System.Type.GetProperty (string, BindingFlags) - // System.Type.GetProperty (string, Type) - // System.Type.GetProperty (string, Type[]) - // System.Type.GetProperty (string, Type, Type[]) - // System.Type.GetProperty (string, Type, Type[], ParameterModifier[]) - // System.Type.GetProperty (string, BindingFlags, Binder, Type, Type[], ParameterModifier[]) - "GetProperty" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System", "String") - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_GetProperty, - - // System.Type.GetProperties (BindingFlags) - "GetProperties" when calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.HasParameterOfType(0, "System.Reflection", "BindingFlags") - && calledMethod.Signature.Length == 1 - && !calledMethod.Signature.IsStatic - => IntrinsicId.Type_GetProperties, - - // static System.Object.GetType () - "GetType" when calledMethod.IsDeclaredOnType("System", "Object") - => IntrinsicId.Object_GetType, - - ".ctor" when calledMethod.IsDeclaredOnType("System.Reflection", "TypeDelegator") - && calledMethod.HasParameterOfType(0, "System", "Type") - => IntrinsicId.TypeDelegator_Ctor, - - "Empty" when calledMethod.IsDeclaredOnType("System", "Array") - => IntrinsicId.Array_Empty, - - // static System.Activator.CreateInstance (System.Type type) - // static System.Activator.CreateInstance (System.Type type, bool nonPublic) - // static System.Activator.CreateInstance (System.Type type, params object?[]? args) - // static System.Activator.CreateInstance (System.Type type, object?[]? args, object?[]? activationAttributes) - // static System.Activator.CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture) - // static System.Activator.CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; } - "CreateInstance" when calledMethod.IsDeclaredOnType("System", "Activator") - && !calledMethod.HasInstantiation - && calledMethod.HasParameterOfType(0, "System", "Type") - => IntrinsicId.Activator_CreateInstance_Type, - - // static System.Activator.CreateInstance (string assemblyName, string typeName) - // static System.Activator.CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) - // static System.Activator.CreateInstance (string assemblyName, string typeName, object?[]? activationAttributes) - "CreateInstance" when calledMethod.IsDeclaredOnType("System", "Activator") - && !calledMethod.HasInstantiation - && calledMethod.HasParameterOfType(0, "System", "String") - && calledMethod.HasParameterOfType(1, "System", "String") - => IntrinsicId.Activator_CreateInstance_AssemblyName_TypeName, - - // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName) - // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // static System.Activator.CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) - "CreateInstanceFrom" when calledMethod.IsDeclaredOnType("System", "Activator") - && !calledMethod.HasInstantiation - && calledMethod.HasParameterOfType(0, "System", "String") - && calledMethod.HasParameterOfType(1, "System", "String") - => IntrinsicId.Activator_CreateInstanceFrom, - - // static T System.Activator.CreateInstance () - "CreateInstance" when calledMethod.IsDeclaredOnType("System", "Activator") - && calledMethod.HasInstantiation - && calledMethod.Instantiation.Length == 1 - && calledMethod.Signature.Length == 0 - => IntrinsicId.Activator_CreateInstanceOfT, - - // System.AppDomain.CreateInstance (string assemblyName, string typeName) - // System.AppDomain.CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // System.AppDomain.CreateInstance (string assemblyName, string typeName, object? []? activationAttributes) - "CreateInstance" when calledMethod.IsDeclaredOnType("System", "AppDomain") - && calledMethod.HasParameterOfType(0, "System", "String") - && calledMethod.HasParameterOfType(1, "System", "String") - => IntrinsicId.AppDomain_CreateInstance, - - // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName) - // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // System.AppDomain.CreateInstanceAndUnwrap (string assemblyName, string typeName, object? []? activationAttributes) - "CreateInstanceAndUnwrap" when calledMethod.IsDeclaredOnType("System", "AppDomain") - && calledMethod.HasParameterOfType(0, "System", "String") - && calledMethod.HasParameterOfType(1, "System", "String") - => IntrinsicId.AppDomain_CreateInstanceAndUnwrap, - - // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName) - // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // System.AppDomain.CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) - "CreateInstanceFrom" when calledMethod.IsDeclaredOnType("System", "AppDomain") - && calledMethod.HasParameterOfType(0, "System", "String") - && calledMethod.HasParameterOfType(1, "System", "String") - => IntrinsicId.AppDomain_CreateInstanceFrom, - - // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName) - // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // System.AppDomain.CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, object? []? activationAttributes) - "CreateInstanceFromAndUnwrap" when calledMethod.IsDeclaredOnType("System", "AppDomain") - && calledMethod.HasParameterOfType(0, "System", "String") - && calledMethod.HasParameterOfType(1, "System", "String") - => IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap, - - // System.Reflection.Assembly.CreateInstance (string typeName) - // System.Reflection.Assembly.CreateInstance (string typeName, bool ignoreCase) - // System.Reflection.Assembly.CreateInstance (string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder? binder, object []? args, CultureInfo? culture, object []? activationAttributes) - "CreateInstance" when calledMethod.IsDeclaredOnType("System.Reflection", "Assembly") - && calledMethod.HasParameterOfType(0, "System", "String") - => IntrinsicId.Assembly_CreateInstance, - - // System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type) - "RunClassConstructor" when calledMethod.IsDeclaredOnType("System.Runtime.CompilerServices", "RuntimeHelpers") - && calledMethod.HasParameterOfType(0, "System", "RuntimeTypeHandle") - => IntrinsicId.RuntimeHelpers_RunClassConstructor, - - // System.Reflection.MethodInfo.MakeGenericMethod (Type[] typeArguments) - "MakeGenericMethod" when calledMethod.IsDeclaredOnType("System.Reflection", "MethodInfo") - && !calledMethod.Signature.IsStatic - && calledMethod.Signature.Length == 1 - => IntrinsicId.MethodInfo_MakeGenericMethod, - - _ => IntrinsicId.None, - }; + var diagnosticContext = new DiagnosticContext(new MessageOrigin(method, offset), !ShouldSuppressAnalysisWarningsForRequires(method.OwningMethod, RequiresUnreferencedCodeAttribute), _logger); + RequireDynamicallyAccessedMembers(diagnosticContext, valueToStore, parameter, parameter.ParameterOrigin); + } } - public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMethod, ILOpcode operation, int offset, ValueNodeList methodParams, out ValueNode methodReturnValue) + public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMethod, ILOpcode operation, int offset, ValueNodeList methodParams, out MultiValue methodReturnValue) { methodReturnValue = null; + MultiValue? maybeMethodReturnValue = null; var callingMethodDefinition = callingMethodBody.OwningMethod; bool shouldEnableReflectionWarnings = !ShouldSuppressAnalysisWarningsForRequires(callingMethodDefinition, RequiresUnreferencedCodeAttribute); - var reflectionContext = new ReflectionPatternContext(_logger, shouldEnableReflectionWarnings, callingMethodBody, offset, new MethodOrigin(calledMethod)); + bool shouldEnableAotWarnings = !ShouldSuppressAnalysisWarningsForRequires(callingMethodDefinition, RequiresDynamicCodeAttribute); DynamicallyAccessedMemberTypes returnValueDynamicallyAccessedMemberTypes = 0; - try - { - - bool requiresDataFlowAnalysis = _flowAnnotations.RequiresDataflowAnalysis(calledMethod); - returnValueDynamicallyAccessedMemberTypes = requiresDataFlowAnalysis ? - _flowAnnotations.GetReturnParameterAnnotation(calledMethod) : 0; - - var intrinsicId = GetIntrinsicIdForMethod(calledMethod); - switch (intrinsicId) - { - case IntrinsicId.IntrospectionExtensions_GetTypeInfo: - { - // typeof(Foo).GetTypeInfo()... will be commonly present in code targeting - // the dead-end reflection refactoring. The call doesn't do anything and we - // don't want to lose the annotation. - methodReturnValue = methodParams[0]; - } - break; - - case IntrinsicId.TypeInfo_AsType: - { - // someType.AsType()... will be commonly present in code targeting - // the dead-end reflection refactoring. The call doesn't do anything and we - // don't want to lose the annotation. - methodReturnValue = methodParams[0]; - } - break; - - case IntrinsicId.TypeDelegator_Ctor: - { - // This is an identity function for analysis purposes - if (operation == ILOpcode.newobj) - methodReturnValue = methodParams[1]; - } - break; - - case IntrinsicId.Array_Empty: + bool requiresDataFlowAnalysis = _annotations.RequiresDataflowAnalysis(calledMethod); + returnValueDynamicallyAccessedMemberTypes = requiresDataFlowAnalysis ? + _annotations.GetReturnParameterAnnotation(calledMethod) : 0; + + var diagnosticContext = new DiagnosticContext(new MessageOrigin(callingMethodBody, offset), shouldEnableReflectionWarnings, _logger); + var handleCallAction = new HandleCallAction(_annotations, _reflectionMarker, diagnosticContext, callingMethodDefinition, new MethodOrigin(calledMethod)); + + var intrinsicId = Intrinsics.GetIntrinsicIdForMethod(calledMethod); + switch (intrinsicId) + { + case IntrinsicId.IntrospectionExtensions_GetTypeInfo: + case IntrinsicId.TypeInfo_AsType: + case IntrinsicId.Type_get_UnderlyingSystemType: + case IntrinsicId.Type_GetTypeFromHandle: + case IntrinsicId.Type_get_TypeHandle: + case IntrinsicId.Type_GetInterface: + case IntrinsicId.Type_get_AssemblyQualifiedName: + case IntrinsicId.RuntimeHelpers_RunClassConstructor: + case var callType when (callType == IntrinsicId.Type_GetConstructors || callType == IntrinsicId.Type_GetMethods || callType == IntrinsicId.Type_GetFields || + callType == IntrinsicId.Type_GetProperties || callType == IntrinsicId.Type_GetEvents || callType == IntrinsicId.Type_GetNestedTypes || callType == IntrinsicId.Type_GetMembers) + && calledMethod.OwningType.IsTypeOf(WellKnownType.System_Type) + && calledMethod.Signature[0].IsTypeOf("System.Reflection.BindingFlags") + && !calledMethod.Signature.IsStatic: + case var fieldPropertyOrEvent when (fieldPropertyOrEvent == IntrinsicId.Type_GetField || fieldPropertyOrEvent == IntrinsicId.Type_GetProperty || fieldPropertyOrEvent == IntrinsicId.Type_GetEvent) + && calledMethod.OwningType.IsTypeOf(WellKnownType.System_Type) + && calledMethod.Signature[0].IsTypeOf(WellKnownType.System_String) + && !calledMethod.Signature.IsStatic: + case var getRuntimeMember when getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod + || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: + case IntrinsicId.Type_GetMember: + case IntrinsicId.Type_GetMethod: + case IntrinsicId.Type_GetNestedType: + case IntrinsicId.Nullable_GetUnderlyingType: + case IntrinsicId.Expression_Property when calledMethod.HasParameterOfType(1, "System.Reflection.MethodInfo"): + case var fieldOrPropertyInstrinsic when fieldOrPropertyInstrinsic == IntrinsicId.Expression_Field || fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property: + case IntrinsicId.Type_get_BaseType: + case IntrinsicId.Type_GetConstructor: + case IntrinsicId.MethodBase_GetMethodFromHandle: + case IntrinsicId.MethodBase_get_MethodHandle: + case IntrinsicId.Type_MakeGenericType: + case IntrinsicId.MethodInfo_MakeGenericMethod: + case IntrinsicId.Expression_Call: + case IntrinsicId.Expression_New: + case IntrinsicId.Type_GetType: + case IntrinsicId.Activator_CreateInstance_Type: + case IntrinsicId.Activator_CreateInstance_AssemblyName_TypeName: + case IntrinsicId.Activator_CreateInstanceFrom: + case var appDomainCreateInstance when appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstance + || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceAndUnwrap + || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFrom + || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap: + case IntrinsicId.Assembly_CreateInstance: + { + var instanceValue = MultiValueLattice.Top; + IReadOnlyList parameterValues = methodParams; + if (!calledMethod.Signature.IsStatic) { - methodReturnValue = new ArrayValue(new ConstIntValue(0), calledMethod.Instantiation[0]); + instanceValue = methodParams[0]; + parameterValues = parameterValues.Skip(1).ToImmutableList(); } - break; + bool result = handleCallAction.Invoke(calledMethod, instanceValue, parameterValues, out methodReturnValue, out _); - case IntrinsicId.Type_GetTypeFromHandle: + // Special case some intrinsics for AOT handling (on top of the trimming handling done in the HandleCallAction) + switch (intrinsicId) { - // Infrastructure piece to support "typeof(Foo)" - if (methodParams[0] is RuntimeTypeHandleValue typeHandle) - methodReturnValue = new SystemTypeValue(typeHandle.TypeRepresented); - else if (methodParams[0] is RuntimeTypeHandleForGenericParameterValue typeHandleForGenericParameter) - { - methodReturnValue = new SystemTypeForGenericParameterValue( - typeHandleForGenericParameter.GenericParameter, - _flowAnnotations.GetGenericParameterAnnotation(typeHandleForGenericParameter.GenericParameter)); - } + case IntrinsicId.Type_MakeGenericType: + case IntrinsicId.MethodInfo_MakeGenericMethod: + CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset), RequiresDynamicCodeAttribute); + break; } - break; - case IntrinsicId.Type_get_TypeHandle: - { - foreach (var value in methodParams[0].UniqueValues()) - { - if (value is SystemTypeValue typeValue) - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new RuntimeTypeHandleValue(typeValue.TypeRepresented)); - else if (value == NullValue.Instance) - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, value); - else - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, UnknownValue.Instance); - } - } - break; + return result; + } - // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle) - // System.Reflection.MethodBase.GetMethodFromHandle (RuntimeMethodHandle handle, RuntimeTypeHandle declaringType) - case IntrinsicId.MethodBase_GetMethodFromHandle: - { - // Infrastructure piece to support "ldtoken method -> GetMethodFromHandle" - if (methodParams[0] is RuntimeMethodHandleValue methodHandle) - methodReturnValue = new SystemReflectionMethodBaseValue(methodHandle.MethodRepresented); - } - break; - - // - // System.Type - // - // Type MakeGenericType (params Type[] typeArguments) - // - case IntrinsicId.Type_MakeGenericType: + case IntrinsicId.None: + { + if (calledMethod.IsPInvoke) { - reflectionContext.AnalyzingPattern(); - foreach (var value in methodParams[0].UniqueValues()) - { - if (value is SystemTypeValue typeValue) - { - if (AnalyzeGenericInstantiationTypeArray(methodParams[1], ref reflectionContext, calledMethod, typeValue.TypeRepresented.GetTypeDefinition().Instantiation)) - { - reflectionContext.RecordHandledPattern(); - } - else - { - bool hasUncheckedAnnotation = false; - foreach (GenericParameterDesc genericParameter in typeValue.TypeRepresented.GetTypeDefinition().Instantiation) - { - if (_flowAnnotations.GetGenericParameterAnnotation(genericParameter) != DynamicallyAccessedMemberTypes.None || - (genericParameter.HasDefaultConstructorConstraint && !typeValue.TypeRepresented.IsNullable)) - { - // If we failed to analyze the array, we go through the analyses again - // and intentionally ignore one particular annotation: - // Special case: Nullable where T : struct - // The struct constraint in C# implies new() constraints, but Nullable doesn't make a use of that part. - // There are several places even in the framework where typeof(Nullable<>).MakeGenericType would warn - // without any good reason to do so. - hasUncheckedAnnotation = true; - break; - } - } - if (hasUncheckedAnnotation) - { - reflectionContext.RecordUnrecognizedPattern( - (int)DiagnosticId.MakeGenericType, - new DiagnosticString(DiagnosticId.MakeGenericType).GetMessage(calledMethod.GetDisplayName())); - } - } - - // We haven't found any generic parameters with annotations, so there's nothing to validate. - reflectionContext.RecordHandledPattern(); - } - else if (value == NullValue.Instance) - reflectionContext.RecordHandledPattern(); - else - { - // We have no way to "include more" to fix this if we don't know, so we have to warn - reflectionContext.RecordUnrecognizedPattern( - (int)DiagnosticId.MakeGenericType, - new DiagnosticString(DiagnosticId.MakeGenericType).GetMessage(calledMethod.GetDisplayName())); - } - } - - CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset), RequiresDynamicCodeAttribute); + // Is the PInvoke dangerous? + ParameterMetadata[] paramMetadata = calledMethod.GetParameterMetadata(); - // We don't want to lose track of the type - // in case this is e.g. Activator.CreateInstance(typeof(Foo<>).MakeGenericType(...)); - methodReturnValue = methodParams[0]; - } - break; - - // - // System.Reflection.RuntimeReflectionExtensions - // - // static GetRuntimeEvent (this Type type, string name) - // static GetRuntimeField (this Type type, string name) - // static GetRuntimeMethod (this Type type, string name, Type[] parameters) - // static GetRuntimeProperty (this Type type, string name) - // - case var getRuntimeMember when getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent - || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField - || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod - || getRuntimeMember == IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: - { + ParameterMetadata returnParamMetadata = Array.Find(paramMetadata, m => m.Index == 0); - reflectionContext.AnalyzingPattern(); - BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; - DynamicallyAccessedMemberTypes requiredMemberTypes = getRuntimeMember switch - { - IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent => DynamicallyAccessedMemberTypes.PublicEvents, - IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField => DynamicallyAccessedMemberTypes.PublicFields, - IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod => DynamicallyAccessedMemberTypes.PublicMethods, - IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty => DynamicallyAccessedMemberTypes.PublicProperties, - _ => throw new Exception($"Reflection call '{calledMethod.GetDisplayName()}' inside '{callingMethodDefinition.GetDisplayName()}' is of unexpected member type."), - }; - - foreach (var value in methodParams[0].UniqueValues()) + bool comDangerousMethod = IsComInterop(returnParamMetadata.MarshalAsDescriptor, calledMethod.Signature.ReturnType); + for (int paramIndex = 0; paramIndex < calledMethod.Signature.Length; paramIndex++) { - if (value is SystemTypeValue systemTypeValue) - { - foreach (var stringParam in methodParams[1].UniqueValues()) - { - if (stringParam is KnownStringValue stringValue) - { - switch (getRuntimeMember) - { - case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeEvent: - MarkEventsOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, e => e.Name == stringValue.Contents, bindingFlags); - reflectionContext.RecordHandledPattern(); - break; - case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeField: - MarkFieldsOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, f => f.Name == stringValue.Contents, bindingFlags); - reflectionContext.RecordHandledPattern(); - break; - case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeMethod: - ProcessGetMethodByName(ref reflectionContext, systemTypeValue.TypeRepresented, stringValue.Contents, bindingFlags, ref methodReturnValue); - reflectionContext.RecordHandledPattern(); - break; - case IntrinsicId.RuntimeReflectionExtensions_GetRuntimeProperty: - MarkPropertiesOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, p => p.Name == stringValue.Contents, bindingFlags); - reflectionContext.RecordHandledPattern(); - break; - default: - throw new Exception($"Error processing reflection call '{calledMethod.GetDisplayName()}' inside {callingMethodDefinition.GetDisplayName()}. Unexpected member kind."); - } - } - else - { - RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new ParameterOrigin(calledMethod, 0)); - } - } - } - else + MarshalAsDescriptor? marshalAsDescriptor = null; + for (int metadataIndex = 0; metadataIndex < paramMetadata.Length; metadataIndex++) { - RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new ParameterOrigin(calledMethod, 0)); + if (paramMetadata[metadataIndex].Index == paramIndex + 1) + marshalAsDescriptor = paramMetadata[metadataIndex].MarshalAsDescriptor; } - } - } - break; - - // - // System.Linq.Expressions.Expression - // - // static Call (Type, String, Type[], Expression[]) - // - case IntrinsicId.Expression_Call: - { - reflectionContext.AnalyzingPattern(); - BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; - bool hasTypeArguments = (methodParams[2] as ArrayValue)?.Size.AsConstInt() != 0; - foreach (var value in methodParams[0].UniqueValues()) - { - if (value is SystemTypeValue systemTypeValue) - { - foreach (var stringParam in methodParams[1].UniqueValues()) - { - if (stringParam is KnownStringValue stringValue) - { - foreach (var method in systemTypeValue.TypeRepresented.GetMethodsOnTypeHierarchy(m => m.Name == stringValue.Contents, bindingFlags)) - { - ValidateGenericMethodInstantiation(ref reflectionContext, method, methodParams[2], calledMethod); - MarkMethod(ref reflectionContext, method); - } - - reflectionContext.RecordHandledPattern(); - } - else - { - if (hasTypeArguments) - { - // We don't know what method the `MakeGenericMethod` was called on, so we have to assume - // that the method may have requirements which we can't fullfil -> warn. - reflectionContext.RecordUnrecognizedPattern( - (int)DiagnosticId.MakeGenericMethod, - new DiagnosticString(DiagnosticId.MakeGenericMethod).GetMessage(DiagnosticUtilities.GetMethodSignatureDisplayName(calledMethod))); - } - - RequireDynamicallyAccessedMembers( - ref reflectionContext, - GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags), - value, - new ParameterOrigin(calledMethod, 0)); - } - } - } - else - { - if (hasTypeArguments) - { - // We don't know what method the `MakeGenericMethod` was called on, so we have to assume - // that the method may have requirements which we can't fullfil -> warn. - reflectionContext.RecordUnrecognizedPattern( - (int)DiagnosticId.MakeGenericMethod, - new DiagnosticString(DiagnosticId.MakeGenericMethod).GetMessage(DiagnosticUtilities.GetMethodSignatureDisplayName(calledMethod))); - } - - RequireDynamicallyAccessedMembers( - ref reflectionContext, - GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags), - value, - new ParameterOrigin(calledMethod, 0)); - } - } - } - break; - - // - // System.Linq.Expressions.Expression - // - // static Property (Expression, MethodInfo) - // - case IntrinsicId.Expression_Property when calledMethod.HasParameterOfType(1, "System.Reflection", "MethodInfo"): - { - reflectionContext.AnalyzingPattern(); - foreach (var value in methodParams[1].UniqueValues()) - { - if (value is SystemReflectionMethodBaseValue methodBaseValue) - { - // We have one of the accessors for the property. The Expression.Property will in this case search - // for the matching PropertyInfo and store that. So to be perfectly correct we need to mark the - // respective PropertyInfo as "accessed via reflection". - var propertyDefinition = methodBaseValue.MethodRepresented.GetPropertyForAccessor(); - if (propertyDefinition is not null) - { - MarkProperty(ref reflectionContext, propertyDefinition); - continue; - } - } - else if (value == NullValue.Instance) - { - reflectionContext.RecordHandledPattern(); - continue; - } - // In all other cases we may not even know which type this is about, so there's nothing we can do - // report it as a warning. - reflectionContext.RecordUnrecognizedPattern( - (int)DiagnosticId.PropertyAccessorParameterInLinqExpressionsCannotBeStaticallyDetermined, - new DiagnosticString(DiagnosticId.PropertyAccessorParameterInLinqExpressionsCannotBeStaticallyDetermined).GetMessage( - DiagnosticUtilities.GetParameterNameForErrorMessage(new ParameterOrigin(calledMethod, 1)), - DiagnosticUtilities.GetMethodSignatureDisplayName(calledMethod))); + comDangerousMethod |= IsComInterop(marshalAsDescriptor, calledMethod.Signature[paramIndex]); } - } - break; - - // - // System.Linq.Expressions.Expression - // - // static Field (Expression, Type, String) - // static Property (Expression, Type, String) - // - case var fieldOrPropertyInstrinsic when fieldOrPropertyInstrinsic == IntrinsicId.Expression_Field || fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property: - { - reflectionContext.AnalyzingPattern(); - DynamicallyAccessedMemberTypes memberTypes = fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property - ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties - : DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields; - foreach (var value in methodParams[1].UniqueValues()) + if (comDangerousMethod) { - if (value is SystemTypeValue systemTypeValue) - { - foreach (var stringParam in methodParams[2].UniqueValues()) - { - if (stringParam is KnownStringValue stringValue) - { - BindingFlags bindingFlags = methodParams[0]?.Kind == ValueNodeKind.Null ? BindingFlags.Static : BindingFlags.Default; - if (fieldOrPropertyInstrinsic == IntrinsicId.Expression_Property) - { - MarkPropertiesOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, filter: p => p.Name == stringValue.Contents, bindingFlags); - } - else - { - MarkFieldsOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, filter: f => f.Name == stringValue.Contents, bindingFlags); - } - - reflectionContext.RecordHandledPattern(); - } - else - { - RequireDynamicallyAccessedMembers(ref reflectionContext, memberTypes, value, new ParameterOrigin(calledMethod, 2)); - } - } - } - else - { - RequireDynamicallyAccessedMembers(ref reflectionContext, memberTypes, value, new ParameterOrigin(calledMethod, 1)); - } + diagnosticContext.AddDiagnostic(DiagnosticId.CorrectnessOfCOMCannotBeGuaranteed, calledMethod.GetDisplayName()); } } - break; - - // - // System.Linq.Expressions.Expression - // - // static New (Type) - // - case IntrinsicId.Expression_New: - { - reflectionContext.AnalyzingPattern(); - foreach (var value in methodParams[0].UniqueValues()) - { - if (value is SystemTypeValue systemTypeValue) - { - MarkConstructorsOnType(ref reflectionContext, systemTypeValue.TypeRepresented, null, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - reflectionContext.RecordHandledPattern(); - } - else - { - RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, value, new ParameterOrigin(calledMethod, 0)); - } - } - } - break; - - // - // System.Enum - // - // static GetValues (Type) - // - case IntrinsicId.Enum_GetValues: - { - // Enum.GetValues returns System.Array, but it's the array of the enum type under the hood - // and people depend on this undocumented detail (could have returned enum of the underlying - // type instead). - // - // At least until we have shared enum code, this needs extra handling to get it right. - foreach (var value in methodParams[0].UniqueValues()) - { - if (value is SystemTypeValue systemTypeValue - && !systemTypeValue.TypeRepresented.IsGenericDefinition - && !systemTypeValue.TypeRepresented.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) - { - if (systemTypeValue.TypeRepresented.IsEnum) - { - _dependencies.Add(_factory.ConstructedTypeSymbol(systemTypeValue.TypeRepresented.MakeArrayType()), "Enum.GetValues"); - } - } - else - CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset),RequiresDynamicCodeAttribute); - } - } - break; - - // - // System.Runtime.InteropServices.Marshal - // - // static SizeOf (Type) - // static PtrToStructure (IntPtr, Type) - // static DestroyStructure (IntPtr, Type) - // static OffsetOf (Type, string) - // - case IntrinsicId.Marshal_SizeOf: - case IntrinsicId.Marshal_PtrToStructure: - case IntrinsicId.Marshal_DestroyStructure: - case IntrinsicId.Marshal_OffsetOf: - { - int paramIndex = intrinsicId == IntrinsicId.Marshal_SizeOf - || intrinsicId == IntrinsicId.Marshal_OffsetOf - ? 0 : 1; + var origin = new MessageOrigin(callingMethodBody, offset); + CheckAndReportRequires(calledMethod, origin, RequiresUnreferencedCodeAttribute); + CheckAndReportRequires(calledMethod, origin, RequiresDynamicCodeAttribute); + CheckAndReportRequires(calledMethod, origin, RequiresAssemblyFilesAttribute); - // We need the data to do struct marshalling. - foreach (var value in methodParams[paramIndex].UniqueValues()) - { - if (value is SystemTypeValue systemTypeValue - && !systemTypeValue.TypeRepresented.IsGenericDefinition - && !systemTypeValue.TypeRepresented.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) - { - if (systemTypeValue.TypeRepresented.IsDefType) - { - _dependencies.Add(_factory.StructMarshallingData((DefType)systemTypeValue.TypeRepresented), "Marshal API"); - } - } - else - CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset), RequiresDynamicCodeAttribute); - } - } - break; - - // - // System.Runtime.InteropServices.Marshal - // - // static GetDelegateForFunctionPointer (IntPtr, Type) - // - case IntrinsicId.Marshal_GetDelegateForFunctionPointer: + var instanceValue = MultiValueLattice.Top; + IReadOnlyList parameterValues = methodParams; + if (!calledMethod.Signature.IsStatic) { - // We need the data to do delegate marshalling. - foreach (var value in methodParams[1].UniqueValues()) - { - if (value is SystemTypeValue systemTypeValue - && !systemTypeValue.TypeRepresented.IsGenericDefinition - && !systemTypeValue.TypeRepresented.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) - { - if (systemTypeValue.TypeRepresented.IsDefType) - { - _dependencies.Add(_factory.DelegateMarshallingData((DefType)systemTypeValue.TypeRepresented), "Marshal API"); - } - } - else - CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset), RequiresDynamicCodeAttribute); - } + instanceValue = methodParams[0]; + parameterValues = parameterValues.Skip(1).ToImmutableList(); } - break; - - // - // System.Object - // - // GetType() - // - case IntrinsicId.Object_GetType: - { - foreach (var valueNode in methodParams[0].UniqueValues()) - { - // Note that valueNode can be statically typed in IL as some generic argument type. - // For example: - // void Method(T instance) { instance.GetType().... } - // Currently this case will end up with null StaticType - since there's no typedef for the generic argument type. - // But it could be that T is annotated with for example PublicMethods: - // void Method<[DAM(PublicMethods)] T>(T instance) { instance.GetType().GetMethod("Test"); } - // In this case it's in theory possible to handle it, by treating the T basically as a base class - // for the actual type of "instance". But the analysis for this would be pretty complicated (as the marking - // has to happen on the callsite, which doesn't know that GetType() will be used...). - // For now we're intentionally ignoring this case - it will produce a warning. - // The counter example is: - // Method(new Derived); - // In this case to get correct results, trimmer would have to mark all public methods on Derived. Which - // currently it won't do. - - TypeDesc staticType = valueNode.StaticType; - if (staticType is null || (!staticType.IsDefType && !staticType.IsArray)) - { - // We don't know anything about the type GetType was called on. Track this as a usual "result of a method call without any annotations" - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, DynamicallyAccessedMemberTypes.None)); - } - else if (staticType.IsSealed() || staticType.IsTypeOf("System", "Delegate")) - { - // We can treat this one the same as if it was a typeof() expression - - // We can allow Object.GetType to be modeled as System.Delegate because we keep all methods - // on delegates anyway so reflection on something this approximation would miss is actually safe. - - // We ignore the fact that the type can be annotated (see below for handling of annotated types) - // This means the annotations (if any) won't be applied - instead we rely on the exact knowledge - // of the type. So for example even if the type is annotated with PublicMethods - // but the code calls GetProperties on it - it will work - mark properties, don't mark methods - // since we ignored the fact that it's annotated. - // This can be seen a little bit as a violation of the annotation, but we already have similar cases - // where a parameter is annotated and if something in the method sets a specific known type to it - // we will also make it just work, even if the annotation doesn't match the usage. - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new SystemTypeValue(staticType)); - } - else - { - reflectionContext.AnalyzingPattern(); - - Debug.Assert(staticType is MetadataType || staticType.IsArray); - MetadataType closestMetadataType = staticType is MetadataType mdType ? - mdType : (MetadataType)_factory.TypeSystemContext.GetWellKnownType(WellKnownType.Array); - - var annotation = _flowAnnotations.GetTypeAnnotation(staticType); + return handleCallAction.Invoke(calledMethod, instanceValue, parameterValues, out methodReturnValue, out _); + } - if (annotation != default) - { - _dependencies.Add(_factory.ObjectGetTypeFlowDependencies(closestMetadataType), "GetType called on this type"); - } + case IntrinsicId.TypeDelegator_Ctor: + { + // This is an identity function for analysis purposes + if (operation == ILOpcode.newobj) + AddReturnValue(methodParams[1]); + } + break; - reflectionContext.RecordHandledPattern(); + case IntrinsicId.Array_Empty: + { + AddReturnValue(ArrayValue.Create(0, calledMethod.Instantiation[0])); + } + break; - // Return a value which is "unknown type" with annotation. For now we'll use the return value node - // for the method, which means we're loosing the information about which staticType this - // started with. For now we don't need it, but we can add it later on. - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, annotation)); - } - } - } - break; - - // - // System.Type - // - // GetType (string) - // GetType (string, Boolean) - // GetType (string, Boolean, Boolean) - // GetType (string, Func, Func) - // GetType (string, Func, Func, Boolean) - // GetType (string, Func, Func, Boolean, Boolean) - // - case IntrinsicId.Type_GetType: + // + // System.Enum + // + // static GetValues (Type) + // + case IntrinsicId.Enum_GetValues: + { + // Enum.GetValues returns System.Array, but it's the array of the enum type under the hood + // and people depend on this undocumented detail (could have returned enum of the underlying + // type instead). + // + // At least until we have shared enum code, this needs extra handling to get it right. + foreach (var value in methodParams[0]) { - reflectionContext.AnalyzingPattern(); - - var parameters = calledMethod.Signature; - if ((parameters.Length == 3 && parameters[2].IsWellKnownType(WellKnownType.Boolean) && methodParams[2].AsConstInt() != 0) || - (parameters.Length == 5 && methodParams[4].AsConstInt() != 0)) - { - reflectionContext.RecordUnrecognizedPattern(2096, $"Call to '{calledMethod.GetDisplayName()}' can perform case insensitive lookup of the type, currently ILLink can not guarantee presence of all the matching types"); - break; - } - foreach (var typeNameValue in methodParams[0].UniqueValues()) + if (value is SystemTypeValue systemTypeValue + && !systemTypeValue.RepresentedType.Type.IsGenericDefinition + && !systemTypeValue.RepresentedType.Type.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) { - if (typeNameValue is KnownStringValue knownStringValue) - { - bool found = ILCompiler.DependencyAnalysis.ReflectionMethodBodyScanner.ResolveType(knownStringValue.Contents, ((MetadataType)callingMethodDefinition.OwningType).Module, - callingMethodDefinition.Context, - out TypeDesc foundType, out ModuleDesc referenceModule); - if (!found) - { - // Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. - reflectionContext.RecordHandledPattern(); - } - else - { - // Also add module metadata in case this reference was through a type forward - if (_factory.MetadataManager.CanGenerateMetadata(referenceModule.GetGlobalModuleType())) - _dependencies.Add(_factory.ModuleMetadata(referenceModule), reflectionContext.MemberWithRequirements.ToString()); - - reflectionContext.RecordRecognizedPattern(() => _dependencies.Add(_factory.MaximallyConstructableType(foundType), "Type.GetType reference")); - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new SystemTypeValue(foundType)); - } - } - else if (typeNameValue == NullValue.Instance) - { - reflectionContext.RecordHandledPattern(); - } - else if (typeNameValue is LeafValueWithDynamicallyAccessedMemberNode valueWithDynamicallyAccessedMember && valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes != 0) - { - // Propagate the annotation from the type name to the return value. Annotation on a string value will be fullfilled whenever a value is assigned to the string with annotation. - // So while we don't know which type it is, we can guarantee that it will fulfill the annotation. - reflectionContext.RecordHandledPattern(); - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes)); - } - else + if (systemTypeValue.RepresentedType.Type.IsEnum) { - reflectionContext.RecordUnrecognizedPattern(2057, $"Unrecognized value passed to the parameter 'typeName' of method '{calledMethod.GetDisplayName()}'. It's not possible to guarantee the availability of the target type."); + _reflectionMarker.Dependencies.Add(_factory.ConstructedTypeSymbol(systemTypeValue.RepresentedType.Type.MakeArrayType()), "Enum.GetValues"); } } - + else + CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset), RequiresDynamicCodeAttribute); } - break; - - // - // GetConstructor (Type[]) - // GetConstructor (BindingFlags, Type[]) - // GetConstructor (BindingFlags, Binder, Type[], ParameterModifier []) - // GetConstructor (BindingFlags, Binder, CallingConventions, Type[], ParameterModifier []) - // - case IntrinsicId.Type_GetConstructor: - { - reflectionContext.AnalyzingPattern(); + } + break; - var parameters = calledMethod.Signature; - BindingFlags? bindingFlags; - if (parameters.Length > 1 && calledMethod.Signature[0].IsTypeOf("System.Reflection", "BindingFlags")) - bindingFlags = GetBindingFlagsFromValue(methodParams[1]); - else - // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter - bindingFlags = BindingFlags.Public | BindingFlags.Instance; + // + // System.Runtime.InteropServices.Marshal + // + // static SizeOf (Type) + // static PtrToStructure (IntPtr, Type) + // static DestroyStructure (IntPtr, Type) + // static OffsetOf (Type, string) + // + case IntrinsicId.Marshal_SizeOf: + case IntrinsicId.Marshal_PtrToStructure: + case IntrinsicId.Marshal_DestroyStructure: + case IntrinsicId.Marshal_OffsetOf: + { + int paramIndex = intrinsicId == IntrinsicId.Marshal_SizeOf + || intrinsicId == IntrinsicId.Marshal_OffsetOf + ? 0 : 1; - int? ctorParameterCount = parameters.Length switch - { - 1 => (methodParams[1] as ArrayValue)?.Size.AsConstInt(), - 2 => (methodParams[2] as ArrayValue)?.Size.AsConstInt(), - 4 => (methodParams[3] as ArrayValue)?.Size.AsConstInt(), - 5 => (methodParams[4] as ArrayValue)?.Size.AsConstInt(), - _ => null, - }; - - // Go over all types we've seen - foreach (var value in methodParams[0].UniqueValues()) + // We need the data to do struct marshalling. + foreach (var value in methodParams[paramIndex]) + { + if (value is SystemTypeValue systemTypeValue + && !systemTypeValue.RepresentedType.Type.IsGenericDefinition + && !systemTypeValue.RepresentedType.Type.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) { - if (value is SystemTypeValue systemTypeValue) - { - if (BindingFlagsAreUnsupported(bindingFlags)) - { - RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors, value, new MethodOrigin(calledMethod)); - } - else - { - if (HasBindingFlag(bindingFlags, BindingFlags.Public) && !HasBindingFlag(bindingFlags, BindingFlags.NonPublic) - && ctorParameterCount == 0) - { - MarkConstructorsOnType(ref reflectionContext, systemTypeValue.TypeRepresented, m => m.IsPublic() && m.Signature.Length == 0, bindingFlags); - } - else - { - MarkConstructorsOnType(ref reflectionContext, systemTypeValue.TypeRepresented, null, bindingFlags); - } - } - reflectionContext.RecordHandledPattern(); - } - else + if (systemTypeValue.RepresentedType.Type.IsDefType) { - // Otherwise fall back to the bitfield requirements - var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(bindingFlags); - // We can scope down the public constructors requirement if we know the number of parameters is 0 - if (requiredMemberTypes == DynamicallyAccessedMemberTypes.PublicConstructors && ctorParameterCount == 0) - requiredMemberTypes = DynamicallyAccessedMemberTypes.PublicParameterlessConstructor; - RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new MethodOrigin(calledMethod)); + _reflectionMarker.Dependencies.Add(_factory.StructMarshallingData((DefType)systemTypeValue.RepresentedType.Type), "Marshal API"); } } - } - break; - - // - // GetMethod (string) - // GetMethod (string, BindingFlags) - // GetMethod (string, Type[]) - // GetMethod (string, Type[], ParameterModifier[]) - // GetMethod (string, BindingFlags, Type[]) - // GetMethod (string, BindingFlags, Binder, Type[], ParameterModifier[]) - // GetMethod (string, BindingFlags, Binder, CallingConventions, Type[], ParameterModifier[]) - // GetMethod (string, int, Type[]) - // GetMethod (string, int, Type[], ParameterModifier[]?) - // GetMethod (string, int, BindingFlags, Binder?, Type[], ParameterModifier[]?) - // GetMethod (string, int, BindingFlags, Binder?, CallingConventions, Type[], ParameterModifier[]?) - // - case IntrinsicId.Type_GetMethod: - { - reflectionContext.AnalyzingPattern(); - - BindingFlags? bindingFlags; - if (calledMethod.Signature.Length > 1 && calledMethod.Signature[1].IsTypeOf("System.Reflection", "BindingFlags")) - bindingFlags = GetBindingFlagsFromValue(methodParams[2]); - else if (calledMethod.Signature.Length > 2 && calledMethod.Signature[2].IsTypeOf("System.Reflection", "BindingFlags")) - bindingFlags = GetBindingFlagsFromValue(methodParams[3]); else - // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter - bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; + CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset), RequiresDynamicCodeAttribute); + } + } + break; - var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags); - foreach (var value in methodParams[0].UniqueValues()) + // + // System.Runtime.InteropServices.Marshal + // + // static GetDelegateForFunctionPointer (IntPtr, Type) + // + case IntrinsicId.Marshal_GetDelegateForFunctionPointer: + { + // We need the data to do delegate marshalling. + foreach (var value in methodParams[1]) + { + if (value is SystemTypeValue systemTypeValue + && !systemTypeValue.RepresentedType.Type.IsGenericDefinition + && !systemTypeValue.RepresentedType.Type.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)) { - if (value is SystemTypeValue systemTypeValue) + if (systemTypeValue.RepresentedType.Type.IsDefType) { - foreach (var stringParam in methodParams[1].UniqueValues()) - { - if (stringParam is KnownStringValue stringValue) - { - if (BindingFlagsAreUnsupported(bindingFlags)) - { - RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods, value, new MethodOrigin(calledMethod)); - } - else - { - ProcessGetMethodByName(ref reflectionContext, systemTypeValue.TypeRepresented, stringValue.Contents, bindingFlags, ref methodReturnValue); - } - reflectionContext.RecordHandledPattern(); - } - else - { - // Otherwise fall back to the bitfield requirements - RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new MethodOrigin(calledMethod)); - } - } - } - else - { - // Otherwise fall back to the bitfield requirements - RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new MethodOrigin(calledMethod)); + _reflectionMarker.Dependencies.Add(_factory.DelegateMarshallingData((DefType)systemTypeValue.RepresentedType.Type), "Marshal API"); } } + else + CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset), RequiresDynamicCodeAttribute); } - break; - - // - // GetNestedType (string) - // GetNestedType (string, BindingFlags) - // - case IntrinsicId.Type_GetNestedType: - { - reflectionContext.AnalyzingPattern(); + } + break; - BindingFlags? bindingFlags; - if (calledMethod.Signature.Length > 1 && calledMethod.Signature[1].IsTypeOf("System.Reflection", "BindingFlags")) - bindingFlags = GetBindingFlagsFromValue(methodParams[2]); + // + // System.Object + // + // GetType() + // + case IntrinsicId.Object_GetType: + { + foreach (var valueNode in methodParams[0]) + { + // Note that valueNode can be statically typed in IL as some generic argument type. + // For example: + // void Method(T instance) { instance.GetType().... } + // Currently this case will end up with null StaticType - since there's no typedef for the generic argument type. + // But it could be that T is annotated with for example PublicMethods: + // void Method<[DAM(PublicMethods)] T>(T instance) { instance.GetType().GetMethod("Test"); } + // In this case it's in theory possible to handle it, by treating the T basically as a base class + // for the actual type of "instance". But the analysis for this would be pretty complicated (as the marking + // has to happen on the callsite, which doesn't know that GetType() will be used...). + // For now we're intentionally ignoring this case - it will produce a warning. + // The counter example is: + // Method(new Derived); + // In this case to get correct results, trimmer would have to mark all public methods on Derived. Which + // currently it won't do. + + TypeDesc? staticType = (valueNode as IValueWithStaticType)?.StaticType; + if (staticType is null || (!staticType.IsDefType && !staticType.IsArray)) + { + // We don't know anything about the type GetType was called on. Track this as a usual "result of a method call without any annotations" + AddReturnValue(_annotations.GetMethodReturnValue(calledMethod)); + } + else if (staticType.IsSealed() || staticType.IsTypeOf("System", "Delegate")) + { + // We can treat this one the same as if it was a typeof() expression + + // We can allow Object.GetType to be modeled as System.Delegate because we keep all methods + // on delegates anyway so reflection on something this approximation would miss is actually safe. + + // We ignore the fact that the type can be annotated (see below for handling of annotated types) + // This means the annotations (if any) won't be applied - instead we rely on the exact knowledge + // of the type. So for example even if the type is annotated with PublicMethods + // but the code calls GetProperties on it - it will work - mark properties, don't mark methods + // since we ignored the fact that it's annotated. + // This can be seen a little bit as a violation of the annotation, but we already have similar cases + // where a parameter is annotated and if something in the method sets a specific known type to it + // we will also make it just work, even if the annotation doesn't match the usage. + AddReturnValue(new SystemTypeValue(staticType)); + } else - // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter - bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; - - var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes(bindingFlags); - bool everyParentTypeHasAll = true; - foreach (var value in methodParams[0].UniqueValues()) { - if (value is SystemTypeValue systemTypeValue) - { - foreach (var stringParam in methodParams[1].UniqueValues()) - { - if (stringParam is KnownStringValue stringValue) - { - if (BindingFlagsAreUnsupported(bindingFlags)) - // We have chosen not to populate the methodReturnValue for now - RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, value, new MethodOrigin(calledMethod)); - else - { - MetadataType[] matchingNestedTypes = MarkNestedTypesOnType(ref reflectionContext, systemTypeValue.TypeRepresented, m => m.Name == stringValue.Contents, bindingFlags); - - if (matchingNestedTypes != null) - { - for (int i = 0; i < matchingNestedTypes.Length; i++) - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new SystemTypeValue(matchingNestedTypes[i])); - } - } - reflectionContext.RecordHandledPattern(); - } - else - { - // Otherwise fall back to the bitfield requirements - RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new MethodOrigin(calledMethod)); - } - } - } - else - { - // Otherwise fall back to the bitfield requirements - RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new MethodOrigin(calledMethod)); - } - - if (value is LeafValueWithDynamicallyAccessedMemberNode leafValueWithDynamicallyAccessedMember) - { - if (leafValueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.All) - everyParentTypeHasAll = false; - } - else if (!(value is NullValue || value is SystemTypeValue)) - { - // Known Type values are always OK - either they're fully resolved above and thus the return value - // is set to the known resolved type, or if they're not resolved, they won't exist at runtime - // and will cause exceptions - and thus don't introduce new requirements on marking. - // nulls are intentionally ignored as they will lead to exceptions at runtime - // and thus don't introduce new requirements on marking. - everyParentTypeHasAll = false; - } - } - - // If the parent type (all the possible values) has DynamicallyAccessedMemberTypes.All it means its nested types are also fully marked - // (see MarkStep.MarkEntireType - it will recursively mark entire type on nested types). In that case we can annotate - // the returned type (the nested type) with DynamicallyAccessedMemberTypes.All as well. - // Note it's OK to blindly overwrite any potential annotation on the return value from the method definition - // since DynamicallyAccessedMemberTypes.All is a superset of any other annotation. - if (everyParentTypeHasAll && methodReturnValue == null) - methodReturnValue = new MethodReturnValue(calledMethod, DynamicallyAccessedMemberTypes.All); - } - break; + Debug.Assert(staticType is MetadataType || staticType.IsArray); + MetadataType closestMetadataType = staticType is MetadataType mdType ? + mdType : (MetadataType)_factory.TypeSystemContext.GetWellKnownType(Internal.TypeSystem.WellKnownType.Array); - // - // AssemblyQualifiedName - // - case IntrinsicId.Type_get_AssemblyQualifiedName: - { + var annotation = _annotations.GetTypeAnnotation(staticType); - ValueNode transformedResult = null; - foreach (var value in methodParams[0].UniqueValues()) - { - if (value is LeafValueWithDynamicallyAccessedMemberNode dynamicallyAccessedThing) - { - var annotatedString = new AnnotatedStringValue(dynamicallyAccessedThing.SourceContext, dynamicallyAccessedThing.DynamicallyAccessedMemberTypes); - transformedResult = MergePointValue.MergeValues(transformedResult, annotatedString); - } - else + if (annotation != default) { - transformedResult = null; - break; + _reflectionMarker.Dependencies.Add(_factory.ObjectGetTypeFlowDependencies(closestMetadataType), "GetType called on this type"); } - } - if (transformedResult != null) - { - methodReturnValue = transformedResult; + // Return a value which is "unknown type" with annotation. For now we'll use the return value node + // for the method, which means we're loosing the information about which staticType this + // started with. For now we don't need it, but we can add it later on. + AddReturnValue(_annotations.GetMethodReturnValue(calledMethod, annotation)); } } - break; + } + break; - // - // UnderlyingSystemType - // - case IntrinsicId.Type_get_UnderlyingSystemType: - { - // This is identity for the purposes of the analysis. - methodReturnValue = methodParams[0]; - } - break; - - // - // Type.BaseType - // - case IntrinsicId.Type_get_BaseType: - { - foreach (var value in methodParams[0].UniqueValues()) - { - if (value is LeafValueWithDynamicallyAccessedMemberNode dynamicallyAccessedMemberNode) - { - DynamicallyAccessedMemberTypes propagatedMemberTypes = DynamicallyAccessedMemberTypes.None; - if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All) - propagatedMemberTypes = DynamicallyAccessedMemberTypes.All; - else - { - // PublicConstructors are not propagated to base type - - if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicEvents)) - propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicEvents; - - if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicFields)) - propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicFields; - - if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicMethods)) - propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicMethods; - - // PublicNestedTypes are not propagated to base type - - // PublicParameterlessConstructor is not propagated to base type - - if (dynamicallyAccessedMemberNode.DynamicallyAccessedMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicProperties)) - propagatedMemberTypes |= DynamicallyAccessedMemberTypes.PublicProperties; - } - - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, propagatedMemberTypes)); - } - else if (value is SystemTypeValue systemTypeValue) - { - DefType baseTypeDefinition = systemTypeValue.TypeRepresented.BaseType; - if (baseTypeDefinition != null) - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new SystemTypeValue(baseTypeDefinition)); - else - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, DynamicallyAccessedMemberTypes.None)); - } - else if (value == NullValue.Instance) - { - // Ignore nulls - null.BaseType will fail at runtime, but it has no effect on static analysis - continue; - } - else - { - // Unknown input - propagate a return value without any annotation - we know it's a Type but we know nothing about it - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, DynamicallyAccessedMemberTypes.None)); - } - } - } - break; - - // - // GetField (string) - // GetField (string, BindingFlags) - // GetEvent (string) - // GetEvent (string, BindingFlags) - // GetProperty (string) - // GetProperty (string, BindingFlags) - // GetProperty (string, Type) - // GetProperty (string, Type[]) - // GetProperty (string, Type, Type[]) - // GetProperty (string, Type, Type[], ParameterModifier[]) - // GetProperty (string, BindingFlags, Binder, Type, Type[], ParameterModifier[]) - // - case var fieldPropertyOrEvent when (fieldPropertyOrEvent == IntrinsicId.Type_GetField || fieldPropertyOrEvent == IntrinsicId.Type_GetProperty || fieldPropertyOrEvent == IntrinsicId.Type_GetEvent) - && calledMethod.IsDeclaredOnType("System", "Type") - && !calledMethod.Signature.IsStatic - && calledMethod.Signature[0].IsString: - { - - reflectionContext.AnalyzingPattern(); - BindingFlags? bindingFlags; - if (calledMethod.Signature.Length > 1 && calledMethod.Signature[1].IsTypeOf("System.Reflection", "BindingFlags")) - bindingFlags = GetBindingFlagsFromValue(methodParams[2]); - else - // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter - bindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; - - DynamicallyAccessedMemberTypes memberTypes = fieldPropertyOrEvent switch - { - IntrinsicId.Type_GetEvent => GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents(bindingFlags), - IntrinsicId.Type_GetField => GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields(bindingFlags), - IntrinsicId.Type_GetProperty => GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties(bindingFlags), - _ => throw new ArgumentException($"Reflection call '{calledMethod.GetDisplayName()}' inside '{callingMethodDefinition.GetDisplayName()}' is of unexpected member type."), - }; - - foreach (var value in methodParams[0].UniqueValues()) - { - if (value is SystemTypeValue systemTypeValue) - { - foreach (var stringParam in methodParams[1].UniqueValues()) - { - if (stringParam is KnownStringValue stringValue) - { - switch (fieldPropertyOrEvent) - { - case IntrinsicId.Type_GetEvent: - if (BindingFlagsAreUnsupported(bindingFlags)) - RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents, value, new MethodOrigin(calledMethod)); - else - MarkEventsOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, filter: e => e.Name == stringValue.Contents, bindingFlags); - break; - case IntrinsicId.Type_GetField: - if (BindingFlagsAreUnsupported(bindingFlags)) - RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields, value, new MethodOrigin(calledMethod)); - else - MarkFieldsOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, filter: f => f.Name == stringValue.Contents, bindingFlags); - break; - case IntrinsicId.Type_GetProperty: - if (BindingFlagsAreUnsupported(bindingFlags)) - RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties, value, new MethodOrigin(calledMethod)); - else - MarkPropertiesOnTypeHierarchy(ref reflectionContext, systemTypeValue.TypeRepresented, filter: p => p.Name == stringValue.Contents, bindingFlags); - break; - default: - Debug.Fail("Unreachable."); - break; - } - reflectionContext.RecordHandledPattern(); - } - else - { - RequireDynamicallyAccessedMembers(ref reflectionContext, memberTypes, value, new MethodOrigin(calledMethod)); - } - } - } - else - { - RequireDynamicallyAccessedMembers(ref reflectionContext, memberTypes, value, new MethodOrigin(calledMethod)); - } - } - } - break; - - // - // GetConstructors (BindingFlags) - // GetMethods (BindingFlags) - // GetFields (BindingFlags) - // GetEvents (BindingFlags) - // GetProperties (BindingFlags) - // GetNestedTypes (BindingFlags) - // GetMembers (BindingFlags) - // - case var callType when (callType == IntrinsicId.Type_GetConstructors || callType == IntrinsicId.Type_GetMethods || callType == IntrinsicId.Type_GetFields || - callType == IntrinsicId.Type_GetProperties || callType == IntrinsicId.Type_GetEvents || callType == IntrinsicId.Type_GetNestedTypes || callType == IntrinsicId.Type_GetMembers) - && calledMethod.IsDeclaredOnType("System", "Type") - && calledMethod.Signature[0].IsTypeOf("System.Reflection", "BindingFlags") - && !calledMethod.Signature.IsStatic: - { - reflectionContext.AnalyzingPattern(); - BindingFlags? bindingFlags; - bindingFlags = GetBindingFlagsFromValue(methodParams[1]); - DynamicallyAccessedMemberTypes memberTypes = DynamicallyAccessedMemberTypes.None; - if (BindingFlagsAreUnsupported(bindingFlags)) - { - memberTypes = callType switch - { - IntrinsicId.Type_GetConstructors => DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors, - IntrinsicId.Type_GetMethods => DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods, - IntrinsicId.Type_GetEvents => DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents, - IntrinsicId.Type_GetFields => DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields, - IntrinsicId.Type_GetProperties => DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties, - IntrinsicId.Type_GetNestedTypes => DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, - IntrinsicId.Type_GetMembers => DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | - DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | - DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | - DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | - DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes, - _ => throw new ArgumentException($"Reflection call '{calledMethod.GetDisplayName()}' inside '{callingMethodDefinition.GetDisplayName()}' is of unexpected member type."), - }; - } - else - { - memberTypes = callType switch - { - IntrinsicId.Type_GetConstructors => GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(bindingFlags), - IntrinsicId.Type_GetMethods => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags), - IntrinsicId.Type_GetEvents => GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents(bindingFlags), - IntrinsicId.Type_GetFields => GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields(bindingFlags), - IntrinsicId.Type_GetProperties => GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties(bindingFlags), - IntrinsicId.Type_GetNestedTypes => GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes(bindingFlags), - IntrinsicId.Type_GetMembers => GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers(bindingFlags), - _ => throw new ArgumentException($"Reflection call '{calledMethod.GetDisplayName()}' inside '{callingMethodDefinition.GetDisplayName()}' is of unexpected member type."), - }; - } - - foreach (var value in methodParams[0].UniqueValues()) - { - RequireDynamicallyAccessedMembers(ref reflectionContext, memberTypes, value, new MethodOrigin(calledMethod)); - } - } - break; - - - // - // GetMember (String) - // GetMember (String, BindingFlags) - // GetMember (String, MemberTypes, BindingFlags) - // - case IntrinsicId.Type_GetMember: - { - reflectionContext.AnalyzingPattern(); - var signature = calledMethod.Signature; - BindingFlags? bindingFlags; - if (signature.Length == 1) - { - // Assume a default value for BindingFlags for methods that don't use BindingFlags as a parameter - bindingFlags = BindingFlags.Public | BindingFlags.Instance; - } - else if (signature.Length == 2 && calledMethod.HasParameterOfType(1, "System.Reflection", "BindingFlags")) - bindingFlags = GetBindingFlagsFromValue(methodParams[2]); - else if (signature.Length == 3 && calledMethod.HasParameterOfType(2, "System.Reflection", "BindingFlags")) - { - bindingFlags = GetBindingFlagsFromValue(methodParams[3]); - } - else // Non recognized intrinsic - throw new ArgumentException($"Reflection call '{calledMethod.GetDisplayName()}' inside '{callingMethodDefinition.GetDisplayName()}' is an unexpected intrinsic."); - - DynamicallyAccessedMemberTypes requiredMemberTypes = DynamicallyAccessedMemberTypes.None; - if (BindingFlagsAreUnsupported(bindingFlags)) - { - requiredMemberTypes = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | - DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | - DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | - DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | - DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; - } - else - { - requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers(bindingFlags); - } - // Go over all types we've seen - foreach (var value in methodParams[0].UniqueValues()) - { - // Mark based on bitfield requirements - RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new MethodOrigin(calledMethod)); - } - } - break; - - // - // GetInterface (String) - // GetInterface (String, bool) - // - case IntrinsicId.Type_GetInterface: - { - reflectionContext.AnalyzingPattern(); - foreach (var value in methodParams[0].UniqueValues()) - { - // For now no support for marking a single interface by name. We would have to correctly support - // mangled names for generics to do that correctly. Simply mark all interfaces on the type for now. - // Require Interfaces annotation - RequireDynamicallyAccessedMembers(ref reflectionContext, DynamicallyAccessedMemberTypes.Interfaces, value, new MethodOrigin(calledMethod)); - // Interfaces is transitive, so the return values will always have at least Interfaces annotation - DynamicallyAccessedMemberTypes returnMemberTypes = DynamicallyAccessedMemberTypes.Interfaces; - // Propagate All annotation across the call - All is a superset of Interfaces - if (value is LeafValueWithDynamicallyAccessedMemberNode annotatedNode - && annotatedNode.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All) - returnMemberTypes = DynamicallyAccessedMemberTypes.All; - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new MethodReturnValue(calledMethod, returnMemberTypes)); - } - } - break; - - // - // System.Activator - // - // static CreateInstance (System.Type type) - // static CreateInstance (System.Type type, bool nonPublic) - // static CreateInstance (System.Type type, params object?[]? args) - // static CreateInstance (System.Type type, object?[]? args, object?[]? activationAttributes) - // static CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture) - // static CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; } - // - case IntrinsicId.Activator_CreateInstance_Type: - { - var parameters = calledMethod.Signature; - - reflectionContext.AnalyzingPattern(); - - int? ctorParameterCount = null; - BindingFlags bindingFlags = BindingFlags.Instance; - if (parameters.Length > 1) - { - if (parameters[1].IsWellKnownType(WellKnownType.Boolean)) - { - // The overload that takes a "nonPublic" bool - bool nonPublic = true; - if (methodParams[1] is ConstIntValue constInt) - { - nonPublic = constInt.Value != 0; - } - - if (nonPublic) - bindingFlags |= BindingFlags.NonPublic | BindingFlags.Public; - else - bindingFlags |= BindingFlags.Public; - ctorParameterCount = 0; - } - else - { - // Overload that has the parameters as the second or fourth argument - int argsParam = parameters.Length == 2 || parameters.Length == 3 ? 1 : 3; - - if (methodParams.Count > argsParam) - { - if (methodParams[argsParam] is ArrayValue arrayValue && - arrayValue.Size.AsConstInt() != null) - ctorParameterCount = arrayValue.Size.AsConstInt(); - else if (methodParams[argsParam] is NullValue) - ctorParameterCount = 0; - } - - if (parameters.Length > 3) - { - if (methodParams[1].AsConstInt() is int constInt) - bindingFlags |= (BindingFlags)constInt; - else - bindingFlags |= BindingFlags.NonPublic | BindingFlags.Public; - } - else - { - bindingFlags |= BindingFlags.Public; - } - } - } - else - { - // The overload with a single System.Type argument - ctorParameterCount = 0; - bindingFlags |= BindingFlags.Public; - } - - // Go over all types we've seen - foreach (var value in methodParams[0].UniqueValues()) - { - if (value is SystemTypeValue systemTypeValue) - { - // Special case known type values as we can do better by applying exact binding flags and parameter count. - MarkConstructorsOnType(ref reflectionContext, systemTypeValue.TypeRepresented, - ctorParameterCount == null ? null : m => m.Signature.Length == ctorParameterCount, bindingFlags); - reflectionContext.RecordHandledPattern(); - } - else - { - // Otherwise fall back to the bitfield requirements - var requiredMemberTypes = GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(bindingFlags); - - // Special case the public parameterless constructor if we know that there are 0 args passed in - if (ctorParameterCount == 0 && requiredMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicConstructors)) - { - requiredMemberTypes &= ~DynamicallyAccessedMemberTypes.PublicConstructors; - requiredMemberTypes |= DynamicallyAccessedMemberTypes.PublicParameterlessConstructor; - } - RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, value, new ParameterOrigin(calledMethod, 0)); - } - } - } - break; - -#if false - // TODO: niche APIs that we probably shouldn't even have added - // - // System.Activator - // - // static CreateInstance (string assemblyName, string typeName) - // static CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) - // static CreateInstance (string assemblyName, string typeName, object?[]? activationAttributes) - // - case IntrinsicId.Activator_CreateInstance_AssemblyName_TypeName: - ProcessCreateInstanceByName(ref reflectionContext, calledMethod, methodParams); - break; - - // - // System.Activator - // - // static CreateInstanceFrom (string assemblyFile, string typeName) - // static CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // static CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) - // - case IntrinsicId.Activator_CreateInstanceFrom: - ProcessCreateInstanceByName(ref reflectionContext, calledMethod, methodParams); - break; -#endif - -#if false - // We probably don't need this because there's other places within the compiler that ensure this works. - // - // System.Activator - // - // static T CreateInstance () - // - // Note: If the when condition returns false it would be an overload which we don't recognize, so just fall through to the default case - case IntrinsicId.Activator_CreateInstanceOfT when - calledMethod.Instantiation.Length == 1: - { - reflectionContext.AnalyzingPattern(); - - if (genericCalledMethod.GenericArguments[0] is GenericParameter genericParameter && - genericParameter.HasDefaultConstructorConstraint) - { - // This is safe, the linker would have marked the default .ctor already - reflectionContext.RecordHandledPattern(); - break; - } - - RequireDynamicallyAccessedMembers( - ref reflectionContext, - DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, - GetTypeValueNodeFromGenericArgument(genericCalledMethod.GenericArguments[0]), - calledMethodDefinition.GenericParameters[0]); - } - break; -#endif - -#if false - // TODO: niche APIs that we probably shouldn't even have added - // - // System.AppDomain - // - // CreateInstance (string assemblyName, string typeName) - // CreateInstance (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // CreateInstance (string assemblyName, string typeName, object? []? activationAttributes) - // - // CreateInstanceAndUnwrap (string assemblyName, string typeName) - // CreateInstanceAndUnwrap (string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // CreateInstanceAndUnwrap (string assemblyName, string typeName, object? []? activationAttributes) - // - // CreateInstanceFrom (string assemblyFile, string typeName) - // CreateInstanceFrom (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // CreateInstanceFrom (string assemblyFile, string typeName, object? []? activationAttributes) - // - // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName) - // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object? []? args, System.Globalization.CultureInfo? culture, object? []? activationAttributes) - // CreateInstanceFromAndUnwrap (string assemblyFile, string typeName, object? []? activationAttributes) - // - case var appDomainCreateInstance when appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstance - || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceAndUnwrap - || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFrom - || appDomainCreateInstance == IntrinsicId.AppDomain_CreateInstanceFromAndUnwrap: - ProcessCreateInstanceByName(ref reflectionContext, calledMethod, methodParams); - break; -#endif - - // - // System.Reflection.Assembly - // - // CreateInstance (string typeName) - // CreateInstance (string typeName, bool ignoreCase) - // CreateInstance (string typeName, bool ignoreCase, BindingFlags bindingAttr, Binder? binder, object []? args, CultureInfo? culture, object []? activationAttributes) - // - case IntrinsicId.Assembly_CreateInstance: - // For now always fail since we don't track assemblies (dotnet/linker/issues/1947) - reflectionContext.AnalyzingPattern(); - reflectionContext.RecordUnrecognizedPattern(2058, $"Parameters passed to method '{calledMethod.GetDisplayName()}' cannot be analyzed. Consider using methods 'System.Type.GetType' and `System.Activator.CreateInstance` instead."); - break; - - // - // System.Runtime.CompilerServices.RuntimeHelpers - // - // RunClassConstructor (RuntimeTypeHandle type) - // - case IntrinsicId.RuntimeHelpers_RunClassConstructor: - { - reflectionContext.AnalyzingPattern(); - foreach (var typeHandleValue in methodParams[0].UniqueValues()) - { - if (typeHandleValue is RuntimeTypeHandleValue runtimeTypeHandleValue) - { - TypeDesc typeRepresented = runtimeTypeHandleValue.TypeRepresented; - if (!typeRepresented.IsGenericDefinition && !typeRepresented.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true) && typeRepresented.HasStaticConstructor) - { - _dependencies.Add(_factory.CanonicalEntrypoint(typeRepresented.GetStaticConstructor()), "RunClassConstructor reference"); - } - - reflectionContext.RecordHandledPattern(); - } - else if (typeHandleValue == NullValue.Instance) - reflectionContext.RecordHandledPattern(); - else - { - reflectionContext.RecordUnrecognizedPattern(2059, $"Unrecognized value passed to the parameter 'type' of method '{calledMethod.GetDisplayName()}'. It's not possible to guarantee the availability of the target static constructor."); - } - } - } - break; - - // - // System.Reflection.MethodInfo - // - // MakeGenericMethod (Type[] typeArguments) - // - case IntrinsicId.MethodInfo_MakeGenericMethod: - { - reflectionContext.AnalyzingPattern(); - - foreach (var methodValue in methodParams[0].UniqueValues()) - { - if (methodValue is SystemReflectionMethodBaseValue methodBaseValue) - { - ValidateGenericMethodInstantiation(ref reflectionContext, methodBaseValue.MethodRepresented, methodParams[1], calledMethod); - } - else if (methodValue == NullValue.Instance) - { - reflectionContext.RecordHandledPattern(); - } - else - { - // We don't know what method the `MakeGenericMethod` was called on, so we have to assume - // that the method may have requirements which we can't fullfil -> warn. - reflectionContext.RecordUnrecognizedPattern( - (int)DiagnosticId.MakeGenericMethod, - new DiagnosticString(DiagnosticId.MakeGenericMethod).GetMessage( - DiagnosticUtilities.GetMethodSignatureDisplayName(calledMethod))); - } - } - // MakeGenericMethod doesn't change the identity of the MethodBase we're tracking so propagate to the return value - methodReturnValue = methodParams[0]; - - CheckAndReportRequires(calledMethod, new MessageOrigin(callingMethodBody, offset), RequiresDynamicCodeAttribute); - } - break; - - default: - if (calledMethod.IsPInvoke) - { - // Is the PInvoke dangerous? - ParameterMetadata[] paramMetadata = calledMethod.GetParameterMetadata(); - - ParameterMetadata returnParamMetadata = Array.Find(paramMetadata, m => m.Index == 0); - - bool comDangerousMethod = IsComInterop(returnParamMetadata.MarshalAsDescriptor, calledMethod.Signature.ReturnType); - for (int paramIndex = 0; paramIndex < calledMethod.Signature.Length; paramIndex++) - { - MarshalAsDescriptor marshalAsDescriptor = null; - for (int metadataIndex = 0; metadataIndex < paramMetadata.Length; metadataIndex++) - { - if (paramMetadata[metadataIndex].Index == paramIndex + 1) - marshalAsDescriptor = paramMetadata[metadataIndex].MarshalAsDescriptor; - } - - comDangerousMethod |= IsComInterop(marshalAsDescriptor, calledMethod.Signature[paramIndex]); - } - - if (comDangerousMethod) - { - reflectionContext.AnalyzingPattern(); - reflectionContext.RecordUnrecognizedPattern( - (int)DiagnosticId.CorrectnessOfCOMCannotBeGuaranteed, - new DiagnosticString(DiagnosticId.CorrectnessOfCOMCannotBeGuaranteed).GetMessage(DiagnosticUtilities.GetMethodSignatureDisplayName(calledMethod))); - } - } - - if (requiresDataFlowAnalysis) - { - reflectionContext.AnalyzingPattern(); - for (int parameterIndex = 0; parameterIndex < methodParams.Count; parameterIndex++) - { - var requiredMemberTypes = _flowAnnotations.GetParameterAnnotation(calledMethod, parameterIndex); - if (requiredMemberTypes != 0) - { - Origin targetContext = DiagnosticUtilities.GetMethodParameterFromIndex(calledMethod, parameterIndex); - RequireDynamicallyAccessedMembers(ref reflectionContext, requiredMemberTypes, methodParams[parameterIndex], targetContext); - } - } - - reflectionContext.RecordHandledPattern(); - } - - var origin = new MessageOrigin(callingMethodBody, offset); - CheckAndReportRequires(calledMethod, origin, RequiresUnreferencedCodeAttribute); - CheckAndReportRequires(calledMethod, origin, RequiresDynamicCodeAttribute); - CheckAndReportRequires(calledMethod, origin, RequiresAssemblyFilesAttribute); - - // To get good reporting of errors we need to track the origin of the value for all method calls - // but except Newobj as those are special. - if (!calledMethod.Signature.ReturnType.IsVoid) - { - methodReturnValue = new MethodReturnValue(calledMethod, returnValueDynamicallyAccessedMemberTypes); - - return true; - } - - return false; - } - } - finally - { - reflectionContext.Dispose(); - } + default: + throw new NotImplementedException("Unhandled instrinsic"); + } // If we get here, we handled this as an intrinsic. As a convenience, if the code above // didn't set the return value (and the method has a return value), we will set it to be an // unknown value with the return type of the method. - if (methodReturnValue == null) - { - if (!calledMethod.Signature.ReturnType.IsVoid) - { - methodReturnValue = new MethodReturnValue(calledMethod, returnValueDynamicallyAccessedMemberTypes); - } - } + bool returnsVoid = calledMethod.Signature.ReturnType.IsVoid; + methodReturnValue = maybeMethodReturnValue ?? (returnsVoid ? + MultiValueLattice.Top : + _annotations.GetMethodReturnValue(calledMethod, returnValueDynamicallyAccessedMemberTypes)!); // Validate that the return value has the correct annotations as per the method return value annotations if (returnValueDynamicallyAccessedMemberTypes != 0) { - foreach (var uniqueValue in methodReturnValue.UniqueValues()) + foreach (var uniqueValue in methodReturnValue) { - if (uniqueValue is LeafValueWithDynamicallyAccessedMemberNode methodReturnValueWithMemberTypes) + if (uniqueValue is ValueWithDynamicallyAccessedMembers methodReturnValueWithMemberTypes) { if (!methodReturnValueWithMemberTypes.DynamicallyAccessedMemberTypes.HasFlag(returnValueDynamicallyAccessedMemberTypes)) throw new InvalidOperationException($"Internal linker error: processing of call from {callingMethodDefinition.GetDisplayName()} to {calledMethod.GetDisplayName()} returned value which is not correctly annotated with the expected dynamic member access kinds."); @@ -2313,9 +734,14 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet } return true; + + void AddReturnValue(MultiValue value) + { + maybeMethodReturnValue = (maybeMethodReturnValue is null) ? value : MultiValueLattice.Meet((MultiValue)maybeMethodReturnValue, value); + } } - bool IsComInterop(MarshalAsDescriptor marshalInfoProvider, TypeDesc parameterType) + bool IsComInterop(MarshalAsDescriptor? marshalInfoProvider, TypeDesc parameterType) { // This is best effort. One can likely find ways how to get COM without triggering these alarms. // AsAny marshalling of a struct with an object-typed field would be one, for example. @@ -2346,12 +772,12 @@ bool IsComInterop(MarshalAsDescriptor marshalInfoProvider, TypeDesc parameterTyp while (parameterType.IsParameterizedType) parameterType = ((ParameterizedType)parameterType).ParameterType; - if (parameterType.IsWellKnownType(WellKnownType.Array)) + if (parameterType.IsWellKnownType(Internal.TypeSystem.WellKnownType.Array)) { // System.Array marshals as IUnknown by default return true; } - else if (parameterType.IsWellKnownType(WellKnownType.String) || + else if (parameterType.IsWellKnownType(Internal.TypeSystem.WellKnownType.String) || InteropTypes.IsStringBuilder(context, parameterType)) { // String and StringBuilder are special cased by interop @@ -2368,8 +794,8 @@ bool IsComInterop(MarshalAsDescriptor marshalInfoProvider, TypeDesc parameterTyp // Interface types marshal as COM by default return true; } - else if (parameterType.IsDelegate || parameterType.IsWellKnownType(WellKnownType.MulticastDelegate) - || parameterType == context.GetWellKnownType(WellKnownType.MulticastDelegate).BaseType) + else if (parameterType.IsDelegate || parameterType.IsWellKnownType(Internal.TypeSystem.WellKnownType.MulticastDelegate) + || parameterType == context.GetWellKnownType(Internal.TypeSystem.WellKnownType.MulticastDelegate).BaseType) { // Delegates are special cased by interop return false; @@ -2394,724 +820,10 @@ bool IsComInterop(MarshalAsDescriptor marshalInfoProvider, TypeDesc parameterTyp return false; } - private bool AnalyzeGenericInstantiationTypeArray(ValueNode arrayParam, ref ReflectionPatternContext reflectionContext, MethodDesc calledMethod, Instantiation genericParameters) - { - bool hasRequirements = false; - foreach (GenericParameterDesc genericParameter in genericParameters) - { - if (_flowAnnotations.GetGenericParameterAnnotation(genericParameter) != DynamicallyAccessedMemberTypes.None) - { - hasRequirements = true; - break; - } - } - - // If there are no requirements, then there's no point in warning - if (!hasRequirements) - return true; - - foreach (var typesValue in arrayParam.UniqueValues()) - { - if (typesValue.Kind != ValueNodeKind.Array) - { - return false; - } - ArrayValue array = (ArrayValue)typesValue; - int? size = array.Size.AsConstInt(); - if (size == null || size != genericParameters.Length) - { - return false; - } - bool allIndicesKnown = true; - for (int i = 0; i < size.Value; i++) - { - if (!array.IndexValues.TryGetValue(i, out ValueBasicBlockPair value) || value.Value is null or { Kind: ValueNodeKind.Unknown }) - { - allIndicesKnown = false; - break; - } - } - - if (!allIndicesKnown) - { - return false; - } - - for (int i = 0; i < size.Value; i++) - { - if (array.IndexValues.TryGetValue(i, out ValueBasicBlockPair value)) - { - RequireDynamicallyAccessedMembers( - ref reflectionContext, - _flowAnnotations.GetGenericParameterAnnotation((GenericParameterDesc)genericParameters[i]), - value.Value, - new MethodOrigin(calledMethod)); - } - } - } - return true; - } - -#if false - void ProcessCreateInstanceByName(ref ReflectionPatternContext reflectionContext, MethodDesc calledMethod, ValueNodeList methodParams) + void RequireDynamicallyAccessedMembers(in DiagnosticContext diagnosticContext, in MultiValue value, ValueWithDynamicallyAccessedMembers targetValue, Origin memberWithRequirements) { - reflectionContext.AnalyzingPattern(); - - BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; - bool parameterlessConstructor = true; - if (calledMethod.Parameters.Count == 8 && calledMethod.Parameters[2].ParameterType.MetadataType == MetadataType.Boolean) - { - parameterlessConstructor = false; - bindingFlags = BindingFlags.Instance; - if (methodParams[3].AsConstInt() is int bindingFlagsInt) - bindingFlags |= (BindingFlags)bindingFlagsInt; - else - bindingFlags |= BindingFlags.Public | BindingFlags.NonPublic; - } - - int methodParamsOffset = !calledMethod.Signature.IsStatic ? 1 : 0; - - foreach (var assemblyNameValue in methodParams[methodParamsOffset].UniqueValues()) - { - if (assemblyNameValue is KnownStringValue assemblyNameStringValue) - { - foreach (var typeNameValue in methodParams[methodParamsOffset + 1].UniqueValues()) - { - if (typeNameValue is KnownStringValue typeNameStringValue) - { - var resolvedAssembly = _context.GetLoadedAssembly(assemblyNameStringValue.Contents); - if (resolvedAssembly == null) - { - reflectionContext.RecordUnrecognizedPattern(2061, $"The assembly name '{assemblyNameStringValue.Contents}' passed to method '{calledMethod.GetDisplayName()}' references assembly which is not available."); - continue; - } - - var resolvedType = _context.TypeNameResolver.ResolveTypeName(resolvedAssembly, typeNameStringValue.Contents)?.Resolve(); - if (resolvedType == null) - { - // It's not wrong to have a reference to non-existing type - the code may well expect to get an exception in this case - // Note that we did find the assembly, so it's not a linker config problem, it's either intentional, or wrong versions of assemblies - // but linker can't know that. - reflectionContext.RecordHandledPattern(); - continue; - } - - MarkConstructorsOnType(ref reflectionContext, resolvedType, parameterlessConstructor ? m => m.Parameters.Count == 0 : null, bindingFlags); - } - else - { - reflectionContext.RecordUnrecognizedPattern(2032, $"Unrecognized value passed to the parameter '{calledMethod.Parameters[1].Name}' of method '{calledMethod.GetDisplayName()}'. It's not possible to guarantee the availability of the target type."); - } - } - } - else - { - reflectionContext.RecordUnrecognizedPattern(2032, $"Unrecognized value passed to the parameter '{calledMethod.Parameters[0].Name}' of method '{calledMethod.GetDisplayName()}'. It's not possible to guarantee the availability of the target type."); - } - } - } -#endif - - void ProcessGetMethodByName( - ref ReflectionPatternContext reflectionContext, - TypeDesc typeDefinition, - string methodName, - BindingFlags? bindingFlags, - ref ValueNode methodReturnValue) - { - bool foundAny = false; - foreach (var method in typeDefinition.GetMethodsOnTypeHierarchy(m => m.Name == methodName, bindingFlags)) - { - MarkMethod(ref reflectionContext, method); - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, new SystemReflectionMethodBaseValue(method)); - foundAny = true; - } - // If there were no methods found the API will return null at runtime, so we should - // track the null as a return value as well. - // This also prevents warnings in such case, since if we don't set the return value it will be - // "unknown" and consumers may warn. - if (!foundAny) - methodReturnValue = MergePointValue.MergeValues(methodReturnValue, NullValue.Instance); - } - - public static DynamicallyAccessedMemberTypes GetMissingMemberTypes(DynamicallyAccessedMemberTypes requiredMemberTypes, DynamicallyAccessedMemberTypes availableMemberTypes) - { - if (availableMemberTypes.HasFlag(requiredMemberTypes)) - return DynamicallyAccessedMemberTypes.None; - - if (requiredMemberTypes == DynamicallyAccessedMemberTypes.All) - return DynamicallyAccessedMemberTypes.All; - - var missingMemberTypes = requiredMemberTypes & ~availableMemberTypes; - - // PublicConstructors is a special case since its value is 3 - so PublicParameterlessConstructor (1) | _PublicConstructor_WithMoreThanOneParameter_ (2) - // The above bit logic only works for value with single bit set. - if (requiredMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicConstructors) && - !availableMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicConstructors)) - missingMemberTypes |= DynamicallyAccessedMemberTypes.PublicConstructors; - - return missingMemberTypes; - } - - private string GetMemberTypesString(DynamicallyAccessedMemberTypes memberTypes) - { - Debug.Assert(memberTypes != DynamicallyAccessedMemberTypes.None); - - if (memberTypes == DynamicallyAccessedMemberTypes.All) - return $"'{nameof(DynamicallyAccessedMemberTypes)}.{nameof(DynamicallyAccessedMemberTypes.All)}'"; - - var memberTypesList = Enum.GetValues() - .Where(damt => (memberTypes & damt) == damt && damt != DynamicallyAccessedMemberTypes.None) - .ToList(); - - if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicConstructors)) - memberTypesList.Remove(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor); - - return string.Join(", ", memberTypesList.Select(mt => $"'{nameof(DynamicallyAccessedMemberTypes)}.{mt}'")); + var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(_reflectionMarker, diagnosticContext, memberWithRequirements); + requireDynamicallyAccessedMembersAction.Invoke(value, targetValue); } - - void RequireDynamicallyAccessedMembers(ref ReflectionPatternContext reflectionContext, DynamicallyAccessedMemberTypes requiredMemberTypes, ValueNode value, Origin targetContext) - { - foreach (var uniqueValue in value.UniqueValues()) - { - if (requiredMemberTypes == DynamicallyAccessedMemberTypes.PublicParameterlessConstructor - && uniqueValue is SystemTypeForGenericParameterValue genericParam - && genericParam.GenericParameter.HasDefaultConstructorConstraint) - { - // We allow a new() constraint on a generic parameter to satisfy DynamicallyAccessedMemberTypes.PublicParameterlessConstructor - reflectionContext.RecordHandledPattern(); - } - else if (uniqueValue is LeafValueWithDynamicallyAccessedMemberNode valueWithDynamicallyAccessedMember) - { - var availableMemberTypes = valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes; - var missingMemberTypesValue = GetMissingMemberTypes(requiredMemberTypes, availableMemberTypes); - if (missingMemberTypesValue != DynamicallyAccessedMemberTypes.None) - { - var missingMemberTypes = GetMemberTypesString(missingMemberTypesValue); - switch ((valueWithDynamicallyAccessedMember.SourceContext, targetContext)) - { - case (ParameterOrigin sourceParameter, ParameterOrigin targetParameter): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsParameter).GetMessage( - DiagnosticUtilities.GetParameterNameForErrorMessage(targetParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName(targetParameter.Method), - DiagnosticUtilities.GetParameterNameForErrorMessage(sourceParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName(sourceParameter.Method), - missingMemberTypes)); - break; - case (ParameterOrigin sourceParameter, MethodReturnOrigin targetMethodReturnType): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsMethodReturnType, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsMethodReturnType).GetMessage( - DiagnosticUtilities.GetMethodSignatureDisplayName(targetMethodReturnType.Method), - DiagnosticUtilities.GetParameterNameForErrorMessage(sourceParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName(sourceParameter.Method), - missingMemberTypes)); - break; - case (ParameterOrigin sourceParameter, FieldOrigin targetField): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsField, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsField).GetMessage( - targetField.GetDisplayName(), - DiagnosticUtilities.GetParameterNameForErrorMessage(sourceParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName(sourceParameter.Method), - missingMemberTypes)); - break; - case (ParameterOrigin sourceParameter, MethodOrigin targetMethod): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsThisParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsThisParameter).GetMessage( - targetMethod.GetDisplayName(), - DiagnosticUtilities.GetParameterNameForErrorMessage(sourceParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName(sourceParameter.Method), - missingMemberTypes)); - break; - case (ParameterOrigin sourceParameter, GenericParameterOrigin targetGenericParameter): - // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsGenericParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsGenericParameter).GetMessage( - targetGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(targetGenericParameter), - DiagnosticUtilities.GetParameterNameForErrorMessage(sourceParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName(sourceParameter.Method), - missingMemberTypes)); - break; - - case (MethodReturnOrigin sourceMethodReturnType, ParameterOrigin targetParameter): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsParameter).GetMessage( - DiagnosticUtilities.GetParameterNameForErrorMessage(targetParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName(targetParameter.Method), - DiagnosticUtilities.GetMethodSignatureDisplayName(sourceMethodReturnType.Method), - missingMemberTypes)); - break; - case (MethodReturnOrigin sourceMethodReturnType, MethodReturnOrigin targetMethodReturnType): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsMethodReturnType, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsMethodReturnType).GetMessage( - DiagnosticUtilities.GetMethodSignatureDisplayName(targetMethodReturnType.Method), - DiagnosticUtilities.GetMethodSignatureDisplayName(sourceMethodReturnType.Method), - missingMemberTypes)); - break; - case (MethodReturnOrigin sourceMethodReturnType, FieldOrigin targetField): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsField, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsField).GetMessage( - targetField.GetDisplayName(), - DiagnosticUtilities.GetMethodSignatureDisplayName(sourceMethodReturnType.Method), - missingMemberTypes)); - break; - case (MethodReturnOrigin sourceMethodReturnType, MethodOrigin targetMethod): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsThisParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsThisParameter).GetMessage( - targetMethod.GetDisplayName(), - DiagnosticUtilities.GetMethodSignatureDisplayName(sourceMethodReturnType.Method), - missingMemberTypes)); - break; - case (MethodReturnOrigin sourceMethodReturnType, GenericParameterOrigin targetGenericParameter): - // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsGenericParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsGenericParameter).GetMessage( - targetGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(targetGenericParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName(sourceMethodReturnType.Method), - missingMemberTypes)); - break; - - case (FieldOrigin sourceField, ParameterOrigin targetParameter): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsParameter).GetMessage( - DiagnosticUtilities.GetParameterNameForErrorMessage(targetParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName(targetParameter.Method), - sourceField.GetDisplayName(), - missingMemberTypes)); - break; - case (FieldOrigin sourceField, MethodReturnOrigin targetMethodReturnType): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsMethodReturnType, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsMethodReturnType).GetMessage( - DiagnosticUtilities.GetMethodSignatureDisplayName(targetMethodReturnType.Method), - sourceField.GetDisplayName(), - missingMemberTypes)); - break; - case (FieldOrigin sourceField, FieldOrigin targetField): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsField, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsField).GetMessage( - targetField.GetDisplayName(), - sourceField.GetDisplayName(), - missingMemberTypes)); - break; - case (FieldOrigin sourceField, MethodOrigin targetMethod): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsThisParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsThisParameter).GetMessage( - targetMethod.GetDisplayName(), - sourceField.GetDisplayName(), - missingMemberTypes)); - break; - case (FieldOrigin sourceField, GenericParameterOrigin targetGenericParameter): - // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsGenericParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsGenericParameter).GetMessage( - targetGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(targetGenericParameter), - sourceField.GetDisplayName(), - missingMemberTypes)); - break; - - case (MethodOrigin sourceMethod, ParameterOrigin targetParameter): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsParameter).GetMessage( - DiagnosticUtilities.GetParameterNameForErrorMessage(targetParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName(targetParameter.Method), - sourceMethod.GetDisplayName(), - missingMemberTypes)); - break; - case (MethodOrigin sourceMethod, MethodReturnOrigin targetMethodReturnType): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsMethodReturnType, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsMethodReturnType).GetMessage( - DiagnosticUtilities.GetMethodSignatureDisplayName(targetMethodReturnType.Method), - sourceMethod.GetDisplayName(), - missingMemberTypes)); - break; - case (MethodOrigin sourceMethod, FieldOrigin targetField): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsField, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsField).GetMessage( - targetField.GetDisplayName(), - sourceMethod.GetDisplayName(), - missingMemberTypes)); - break; - case (MethodOrigin sourceMethod, MethodOrigin targetMethod): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsThisParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsThisParameter).GetMessage( - targetMethod.GetDisplayName(), - sourceMethod.GetDisplayName(), - missingMemberTypes)); - break; - case (MethodOrigin sourceMethod, GenericParameterOrigin targetGenericParameter): - // Currently this is never generated, once ILLink supports full analysis of MakeGenericType/MakeGenericMethod this will be used - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsGenericParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsGenericParameter).GetMessage( - targetGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(targetGenericParameter), - sourceMethod.GetDisplayName(), - missingMemberTypes)); - break; - - case (GenericParameterOrigin sourceGenericParameter, ParameterOrigin targetParameter): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsParameter).GetMessage( - DiagnosticUtilities.GetParameterNameForErrorMessage(targetParameter), - DiagnosticUtilities.GetMethodSignatureDisplayName(targetParameter.Method), - sourceGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(sourceGenericParameter), - missingMemberTypes)); - break; - case (GenericParameterOrigin sourceGenericParameter, MethodReturnOrigin targetMethodReturnType): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsMethodReturnType, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsMethodReturnType).GetMessage( - DiagnosticUtilities.GetMethodSignatureDisplayName(targetMethodReturnType.Method), - sourceGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(sourceGenericParameter), - missingMemberTypes)); - break; - case (GenericParameterOrigin sourceGenericParameter, FieldOrigin targetField): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsField, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsField).GetMessage( - targetField.GetDisplayName(), - sourceGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(sourceGenericParameter), - missingMemberTypes)); - break; - case (GenericParameterOrigin sourceGenericParameter, MethodOrigin targetMethod): - // Currently this is never generated, it might be possible one day if we try to validate annotations on results of reflection - // For example code like this should ideally one day generate the warning - // void TestMethod() - // { - // // This passes the T as the "this" parameter to Type.GetMethods() - // typeof(Type).GetMethod("GetMethods").Invoke(typeof(T), new object[] {}); - // } - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsThisParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsThisParameter).GetMessage( - targetMethod.GetDisplayName(), - sourceGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(sourceGenericParameter), - missingMemberTypes)); - break; - case (GenericParameterOrigin sourceGenericParameter, GenericParameterOrigin targetGenericParameter): - reflectionContext.RecordUnrecognizedPattern((int)DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsGenericParameter, - new DiagnosticString(DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsGenericParameter).GetMessage( - targetGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(targetGenericParameter), - sourceGenericParameter.Name, - DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(sourceGenericParameter), - missingMemberTypes)); - break; - - default: - throw new NotImplementedException($"unsupported source context {valueWithDynamicallyAccessedMember.SourceContext} or target context {targetContext}"); - }; - } - else - { - reflectionContext.RecordHandledPattern(); - } - } - else if (uniqueValue is SystemTypeValue systemTypeValue) - { - MarkTypeForDynamicallyAccessedMembers(ref reflectionContext, systemTypeValue.TypeRepresented, requiredMemberTypes); - } - else if (uniqueValue is KnownStringValue knownStringValue) - { - ModuleDesc callingModule = ((reflectionContext.Source as MethodDesc)?.OwningType as MetadataType)?.Module; - - if (!ILCompiler.DependencyAnalysis.ReflectionMethodBodyScanner.ResolveType(knownStringValue.Contents, callingModule, reflectionContext.Source.Context, out TypeDesc foundType, out ModuleDesc referenceModule)) - { - // Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. - reflectionContext.RecordHandledPattern(); - } - else - { - // Also add module metadata in case this reference was through a type forward - if (_factory.MetadataManager.CanGenerateMetadata(referenceModule.GetGlobalModuleType())) - _dependencies.Add(_factory.ModuleMetadata(referenceModule), reflectionContext.MemberWithRequirements.ToString()); - - MarkType(ref reflectionContext, foundType); - MarkTypeForDynamicallyAccessedMembers(ref reflectionContext, foundType, requiredMemberTypes); - } - } - else if (uniqueValue == NullValue.Instance) - { - // Ignore - probably unreachable path as it would fail at runtime anyway. - } - else - { - switch (targetContext) - { - case ParameterOrigin parameterDefinition: - reflectionContext.RecordUnrecognizedPattern( - 2062, - $"Value passed to parameter '{DiagnosticUtilities.GetParameterNameForErrorMessage(parameterDefinition)}' of method '{DiagnosticUtilities.GetMethodSignatureDisplayName(parameterDefinition.Method)}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); - break; - case MethodReturnOrigin methodReturnType: - reflectionContext.RecordUnrecognizedPattern( - 2063, - $"Value returned from method '{DiagnosticUtilities.GetMethodSignatureDisplayName(methodReturnType.Method)}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); - break; - case FieldOrigin fieldDefinition: - reflectionContext.RecordUnrecognizedPattern( - 2064, - $"Value assigned to {fieldDefinition.GetDisplayName()} can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); - break; - case MethodOrigin methodDefinition: - reflectionContext.RecordUnrecognizedPattern( - 2065, - $"Value passed to implicit 'this' parameter of method '{methodDefinition.GetDisplayName()}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); - break; - case GenericParameterOrigin genericParameter: - // Unknown value to generic parameter - this is possible if the generic argumnet fails to resolve - reflectionContext.RecordUnrecognizedPattern( - 2066, - $"Type passed to generic parameter '{genericParameter.Name}' of '{DiagnosticUtilities.GetGenericParameterDeclaringMemberDisplayName(genericParameter)}' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements."); - break; - default: throw new NotImplementedException($"unsupported target context {targetContext.GetType()}"); - }; - } - } - - reflectionContext.RecordHandledPattern(); - } - - static BindingFlags? GetBindingFlagsFromValue(ValueNode parameter) => (BindingFlags?)parameter.AsConstInt(); - - static bool BindingFlagsAreUnsupported(BindingFlags? bindingFlags) - { - if (bindingFlags == null) - return true; - - // Binding flags we understand - const BindingFlags UnderstoodBindingFlags = - BindingFlags.DeclaredOnly | - BindingFlags.Instance | - BindingFlags.Static | - BindingFlags.Public | - BindingFlags.NonPublic | - BindingFlags.FlattenHierarchy | - BindingFlags.ExactBinding; - - // Binding flags that don't affect binding outside InvokeMember (that we don't analyze). - const BindingFlags IgnorableBindingFlags = - BindingFlags.InvokeMethod | - BindingFlags.CreateInstance | - BindingFlags.GetField | - BindingFlags.SetField | - BindingFlags.GetProperty | - BindingFlags.SetProperty; - - BindingFlags flags = bindingFlags.Value; - return (flags & ~(UnderstoodBindingFlags | IgnorableBindingFlags)) != 0; - } - - static bool HasBindingFlag(BindingFlags? bindingFlags, BindingFlags? search) => bindingFlags != null && (bindingFlags & search) == search; - - void MarkTypeForDynamicallyAccessedMembers(ref ReflectionPatternContext reflectionContext, TypeDesc typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes, bool declaredOnly = false) - { - foreach (var member in typeDefinition.GetDynamicallyAccessedMembers(requiredMemberTypes, declaredOnly)) - { - switch (member) - { - case MethodDesc method: - MarkMethod(ref reflectionContext, method); - break; - case FieldDesc field: - MarkField(ref reflectionContext, field); - break; - case MetadataType type: - MarkType(ref reflectionContext, type); - break; - case PropertyPseudoDesc property: - MarkProperty(ref reflectionContext, property); - break; - case EventPseudoDesc @event: - MarkEvent(ref reflectionContext, @event); - break; - default: - Debug.Fail(member.GetType().ToString()); - break; - } - } - } - - void MarkType(ref ReflectionPatternContext reflectionContext, TypeDesc type) - { - RootingHelpers.TryGetDependenciesForReflectedType(ref _dependencies, _factory, type, reflectionContext.MemberWithRequirements.ToString()); - reflectionContext.RecordHandledPattern(); - } - - void WarnOnReflectionAccess(ref ReflectionPatternContext context, TypeSystemEntity entity) - { - if (_purpose == ScanningPurpose.GetTypeDataflow) - { - // Don't check whether the current scope is a RUC type or RUC method because these warnings - // are not suppressed in RUC scopes. Here the scope represents the DynamicallyAccessedMembers - // annotation on a type, not a callsite which uses the annotation. We always want to warn about - // possible reflection access indicated by these annotations. - _logger.LogWarning(context.Source, DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesMemberOnBaseWithDynamicallyAccessedMembers, - ((TypeOrigin)context.MemberWithRequirements).GetDisplayName(), entity.GetDisplayName()); - } - else - { - if (entity is FieldDesc && context.ReportingEnabled) - { - _logger.LogWarning(context.Source, DiagnosticId.DynamicallyAccessedMembersFieldAccessedViaReflection, entity.GetDisplayName()); - } - else - { - Debug.Assert(entity is MethodDesc); - - _logger.LogWarning(context.Source, DiagnosticId.DynamicallyAccessedMembersMethodAccessedViaReflection, entity.GetDisplayName()); - } - } - } - - void MarkMethod(ref ReflectionPatternContext reflectionContext, MethodDesc method) - { - if(method.DoesMethodRequire(RequiresUnreferencedCodeAttribute, out _)) - { - if (_purpose == ScanningPurpose.GetTypeDataflow) - { - _logger.LogWarning(reflectionContext.Source, DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesMemberOnBaseWithRequiresUnreferencedCode, - ((TypeOrigin)reflectionContext.MemberWithRequirements).GetDisplayName(), method.GetDisplayName()); - } - } - - if (_flowAnnotations.ShouldWarnWhenAccessedForReflection(method) && !ShouldSuppressAnalysisWarningsForRequires(method, RequiresUnreferencedCodeAttribute)) - { - WarnOnReflectionAccess(ref reflectionContext, method); - } - - RootingHelpers.TryGetDependenciesForReflectedMethod(ref _dependencies, _factory, method, reflectionContext.MemberWithRequirements.ToString()); - reflectionContext.RecordHandledPattern(); - } - - void MarkField(ref ReflectionPatternContext reflectionContext, FieldDesc field) - { - if (_flowAnnotations.ShouldWarnWhenAccessedForReflection(field) && !ShouldSuppressAnalysisWarningsForRequires(reflectionContext.Source, RequiresUnreferencedCodeAttribute)) - { - WarnOnReflectionAccess(ref reflectionContext, field); - } - - RootingHelpers.TryGetDependenciesForReflectedField(ref _dependencies, _factory, field, reflectionContext.MemberWithRequirements.ToString()); - reflectionContext.RecordHandledPattern(); - } - - void MarkProperty(ref ReflectionPatternContext reflectionContext, PropertyPseudoDesc property) - { - if (property.GetMethod != null) - MarkMethod(ref reflectionContext, property.GetMethod); - if (property.SetMethod != null) - MarkMethod(ref reflectionContext, property.SetMethod); - reflectionContext.RecordHandledPattern(); - } - - void MarkEvent(ref ReflectionPatternContext reflectionContext, EventPseudoDesc @event) - { - if (@event.AddMethod != null) - MarkMethod(ref reflectionContext, @event.AddMethod); - if (@event.RemoveMethod != null) - MarkMethod(ref reflectionContext, @event.RemoveMethod); - reflectionContext.RecordHandledPattern(); - } - - void MarkConstructorsOnType(ref ReflectionPatternContext reflectionContext, TypeDesc type, Func filter, BindingFlags? bindingFlags = null) - { - foreach (var ctor in type.GetConstructorsOnType(filter, bindingFlags)) - MarkMethod(ref reflectionContext, ctor); - } - - void MarkFieldsOnTypeHierarchy(ref ReflectionPatternContext reflectionContext, TypeDesc type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) - { - foreach (var field in type.GetFieldsOnTypeHierarchy(filter, bindingFlags)) - MarkField(ref reflectionContext, field); - } - - MetadataType[] MarkNestedTypesOnType(ref ReflectionPatternContext reflectionContext, TypeDesc type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) - { - var result = new ArrayBuilder(); - - foreach (var nestedType in type.GetNestedTypesOnType(filter, bindingFlags)) - { - result.Add(nestedType); - MarkTypeForDynamicallyAccessedMembers(ref reflectionContext, nestedType, DynamicallyAccessedMemberTypes.All); - } - - return result.ToArray(); - } - - void MarkPropertiesOnTypeHierarchy(ref ReflectionPatternContext reflectionContext, TypeDesc type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) - { - foreach (var property in type.GetPropertiesOnTypeHierarchy(filter, bindingFlags)) - MarkProperty(ref reflectionContext, property); - } - - void MarkEventsOnTypeHierarchy(ref ReflectionPatternContext reflectionContext, TypeDesc type, Func filter, BindingFlags? bindingFlags = BindingFlags.Default) - { - foreach (var @event in type.GetEventsOnTypeHierarchy(filter, bindingFlags)) - MarkEvent(ref reflectionContext, @event); - } - - void ValidateGenericMethodInstantiation( - ref ReflectionPatternContext reflectionContext, - MethodDesc genericMethod, - ValueNode genericParametersArray, - MethodDesc reflectionMethod) - { - if (!genericMethod.HasInstantiation) - { - reflectionContext.RecordHandledPattern(); - return; - } - if (!AnalyzeGenericInstantiationTypeArray(genericParametersArray, ref reflectionContext, reflectionMethod, genericMethod.GetMethodDefinition().Instantiation)) - { - reflectionContext.RecordUnrecognizedPattern( - (int)DiagnosticId.MakeGenericMethod, - new DiagnosticString(DiagnosticId.MakeGenericMethod).GetMessage(DiagnosticUtilities.GetMethodSignatureDisplayName(reflectionMethod))); - } - else - { - reflectionContext.RecordHandledPattern(); - } - } - - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes(BindingFlags? bindingFlags) => - (HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicNestedTypes : DynamicallyAccessedMemberTypes.None) | - (HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicNestedTypes : DynamicallyAccessedMemberTypes.None) | - (BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes : DynamicallyAccessedMemberTypes.None); - - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(BindingFlags? bindingFlags) => - (HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicConstructors : DynamicallyAccessedMemberTypes.None) | - (HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicConstructors : DynamicallyAccessedMemberTypes.None) | - (BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors : DynamicallyAccessedMemberTypes.None); - - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(BindingFlags? bindingFlags) => - (HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicMethods : DynamicallyAccessedMemberTypes.None) | - (HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None) | - (BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods : DynamicallyAccessedMemberTypes.None); - - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields(BindingFlags? bindingFlags) => - (HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicFields : DynamicallyAccessedMemberTypes.None) | - (HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None) | - (BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields : DynamicallyAccessedMemberTypes.None); - - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties(BindingFlags? bindingFlags) => - (HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicProperties : DynamicallyAccessedMemberTypes.None) | - (HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None) | - (BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties : DynamicallyAccessedMemberTypes.None); - - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents(BindingFlags? bindingFlags) => - (HasBindingFlag(bindingFlags, BindingFlags.Public) ? DynamicallyAccessedMemberTypes.PublicEvents : DynamicallyAccessedMemberTypes.None) | - (HasBindingFlag(bindingFlags, BindingFlags.NonPublic) ? DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None) | - (BindingFlagsAreUnsupported(bindingFlags) ? DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents : DynamicallyAccessedMemberTypes.None); - - static DynamicallyAccessedMemberTypes GetDynamicallyAccessedMemberTypesFromBindingFlagsForMembers(BindingFlags? bindingFlags) => - GetDynamicallyAccessedMemberTypesFromBindingFlagsForConstructors(bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForEvents(bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForFields(bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForMethods(bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForProperties(bindingFlags) | - GetDynamicallyAccessedMemberTypesFromBindingFlagsForNestedTypes(bindingFlags); } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionPatternContext.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionPatternContext.cs deleted file mode 100644 index 61a48fe965a0c..0000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionPatternContext.cs +++ /dev/null @@ -1,124 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; -using Internal.IL; -using Internal.TypeSystem; - -using ILLink.Shared; - -namespace ILCompiler.Dataflow -{ - /// - /// Helper struct to pass around context information about reflection pattern - /// as a single parameter (and have a way to extend this in the future if we need to easily). - /// Also implements a simple validation mechanism to check that the code does report patter recognition - /// results for all methods it works on. - /// The promise of the pattern recorder is that for a given reflection method, it will either not talk - /// about it ever, or it will always report recognized/unrecognized. - /// - struct ReflectionPatternContext : IDisposable - { - readonly Logger _logger; - readonly int _ilOffset; - readonly MethodIL _sourceIL; - -#if DEBUG - bool _patternAnalysisAttempted; - bool _patternReported; -#endif - - public TypeSystemEntity Source { get; private set; } - public Origin MemberWithRequirements { get; private set; } - public bool ReportingEnabled { get; private set; } - - public ReflectionPatternContext( - Logger logger, - bool reportingEnabled, - TypeSystemEntity source, - Origin memberWithRequirements) - { - _logger = logger; - ReportingEnabled = reportingEnabled; - Source = source; - MemberWithRequirements = memberWithRequirements; - _ilOffset = 0; - _sourceIL = null; - -#if DEBUG - _patternAnalysisAttempted = false; - _patternReported = false; -#endif - } - - public ReflectionPatternContext( - Logger logger, - bool reportingEnabled, - MethodIL source, - int offset, - Origin memberWithRequirements) - : this(logger, reportingEnabled, source.OwningMethod, memberWithRequirements) - { - _sourceIL = source; - _ilOffset = offset; - } - -#pragma warning disable CA1822 - [Conditional("DEBUG")] - public void AnalyzingPattern() - { -#if DEBUG - _patternAnalysisAttempted = true; -#endif - } - - [Conditional("DEBUG")] - public void RecordHandledPattern() - { -#if DEBUG - _patternReported = true; -#endif - } -#pragma warning restore CA1822 - - public void RecordRecognizedPattern(Action mark) - { -#if DEBUG - if (!_patternAnalysisAttempted) - throw new InvalidOperationException($"Internal error: To correctly report all patterns, when starting to analyze a pattern the AnalyzingPattern must be called first. {Source} -> {MemberWithRequirements}"); - - _patternReported = true; -#endif - - mark(); - } - - public void RecordUnrecognizedPattern(int messageCode, string message) - { -#if DEBUG - if (!_patternAnalysisAttempted) - throw new InvalidOperationException($"Internal error: To correctly report all patterns, when starting to analyze a pattern the AnalyzingPattern must be called first. {Source} -> {MemberWithRequirements}"); - - _patternReported = true; -#endif - - if (ReportingEnabled) - { - if (_sourceIL != null) - _logger.LogWarning(message, messageCode, _sourceIL, _ilOffset, MessageSubCategory.TrimAnalysis); - else - _logger.LogWarning(message, messageCode, Source, MessageSubCategory.TrimAnalysis); - } - } - - public void Dispose() - { -#if DEBUG - if (_patternAnalysisAttempted && !_patternReported) - throw new InvalidOperationException($"Internal error: A reflection pattern was analyzed, but no result was reported. {Source} -> {MemberWithRequirements}"); -#endif - } - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/RequireDynamicallyAccessedMembersAction.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/RequireDynamicallyAccessedMembersAction.cs new file mode 100644 index 0000000000000..69e03cf2d3945 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/RequireDynamicallyAccessedMembersAction.cs @@ -0,0 +1,47 @@ +// 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.CodeAnalysis; +using ILCompiler.Dataflow; +using ILLink.Shared.TypeSystemProxy; +using Internal.TypeSystem; + +#nullable enable + +namespace ILLink.Shared.TrimAnalysis +{ + partial struct RequireDynamicallyAccessedMembersAction + { + readonly ReflectionMarker _reflectionMarker; + readonly Origin _memberWithRequirements; + + public RequireDynamicallyAccessedMembersAction( + ReflectionMarker reflectionMarker, + in DiagnosticContext diagnosticContext, + Origin memberWithRequirements) + { + _reflectionMarker = reflectionMarker; + _diagnosticContext = diagnosticContext; + _memberWithRequirements = memberWithRequirements; + } + + public partial bool TryResolveTypeNameAndMark(string typeName, bool needsAssemblyName, out TypeProxy type) + { + if (_reflectionMarker.TryResolveTypeNameAndMark(typeName, _diagnosticContext.Origin, needsAssemblyName, _memberWithRequirements, out TypeDesc? foundType)) + { + type = new(foundType); + return true; + } + else + { + type = default; + return false; + } + } + + private partial void MarkTypeForDynamicallyAccessedMembers(in TypeProxy type, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) + { + _reflectionMarker.MarkTypeForDynamicallyAccessedMembers(_diagnosticContext.Origin, type.Type, dynamicallyAccessedMemberTypes, _memberWithRequirements); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TypeExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TypeExtensions.cs index 89b9cd764a2e3..d8a0311b5a663 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TypeExtensions.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TypeExtensions.cs @@ -1,9 +1,17 @@ // 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; +using ILLink.Shared.TypeSystemProxy; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; +using TypeSystemWellKnownType = Internal.TypeSystem.WellKnownType; +using ILLinkSharedWellKnownType = ILLink.Shared.TypeSystemProxy.WellKnownType; + +#nullable enable + namespace ILCompiler.Dataflow { static class TypeExtensions @@ -13,14 +21,41 @@ public static bool IsTypeOf(this TypeDesc type, string ns, string name) return type is MetadataType mdType && mdType.Name == name && mdType.Namespace == ns; } - public static bool IsDeclaredOnType(this MethodDesc method, string ns, string name) + public static bool IsTypeOf(this TypeDesc type, string fullTypeName) + { + if (type is not MetadataType metadataType) + return false; + + var name = fullTypeName.AsSpan(); + if (metadataType.Name.Length + 1 > name.Length) + return false; + + if (!name.Slice(name.Length - metadataType.Name.Length).Equals(metadataType.Name.AsSpan(), StringComparison.Ordinal)) + return false; + + if (name[name.Length - metadataType.Name.Length - 1] != '.') + return false; + + return name.Slice(0, name.Length - metadataType.Name.Length - 1).Equals(metadataType.Namespace, StringComparison.Ordinal); + } + + public static bool IsTypeOf(this TypeDesc type, ILLinkSharedWellKnownType wellKnownType) => + wellKnownType switch + { + ILLinkSharedWellKnownType.System_String => type.IsWellKnownType(TypeSystemWellKnownType.String), + ILLinkSharedWellKnownType.System_Object => type.IsWellKnownType(TypeSystemWellKnownType.Object), + ILLinkSharedWellKnownType.System_Void => type.IsWellKnownType(TypeSystemWellKnownType.Void), + _ => wellKnownType == WellKnownTypeExtensions.GetWellKnownType((type as MetadataType)?.Namespace ?? string.Empty, ((type as MetadataType)?.Name) ?? string.Empty) + }; + + public static bool IsDeclaredOnType(this MethodDesc method, string fullTypeName) { - return method.OwningType.IsTypeOf(ns, name); + return method.OwningType.IsTypeOf(fullTypeName); } - public static bool HasParameterOfType(this MethodDesc method, int index, string ns, string name) + public static bool HasParameterOfType(this MethodDesc method, int index, string fullTypeName) { - return index < method.Signature.Length && method.Signature[index].IsTypeOf(ns, name); + return index < method.Signature.Length && method.Signature[index].IsTypeOf(fullTypeName); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TypeProxy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TypeProxy.cs new file mode 100644 index 0000000000000..35943c4c58c43 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TypeProxy.cs @@ -0,0 +1,49 @@ +// 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.Immutable; +using ILCompiler; +using ILCompiler.Dataflow; +using Internal.TypeSystem; + +#nullable enable + +namespace ILLink.Shared.TypeSystemProxy +{ + internal readonly partial struct TypeProxy + { + public TypeProxy(TypeDesc type) => Type = type; + + public static implicit operator TypeProxy(TypeDesc type) => new(type); + + internal partial ImmutableArray GetGenericParameters() + { + var typeDef = Type.GetTypeDefinition(); + + if (!typeDef.HasInstantiation) + return ImmutableArray.Empty; + + var builder = ImmutableArray.CreateBuilder(typeDef.Instantiation.Length); + foreach (var genericParameter in typeDef.Instantiation) + { + builder.Add(new GenericParameterProxy((GenericParameterDesc)genericParameter)); + } + + return builder.ToImmutableArray(); + } + + public TypeDesc Type { get; } + + public string Name { get => Type is MetadataType metadataType ? metadataType.Name : string.Empty; } + + public string? Namespace { get => Type is MetadataType metadataType ? metadataType.Namespace : null; } + + public bool IsTypeOf(string @namespace, string name) => Type.IsTypeOf(@namespace, name); + + public bool IsTypeOf(WellKnownType wellKnownType) => Type.IsTypeOf(wellKnownType); + + public string GetDisplayName() => Type.GetDisplayName(); + + public override string ToString() => Type.ToString(); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ValueNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ValueNode.cs index e348eec7c2f64..9a8254de64c3d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ValueNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ValueNode.cs @@ -3,1325 +3,14 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text; -using Internal.IL; -using Internal.TypeSystem; -using Internal.TypeSystem.Ecma; +using MultiValue = ILLink.Shared.DataFlow.ValueSet; -using Debug = System.Diagnostics.Debug; +#nullable enable namespace ILCompiler.Dataflow { - public enum ValueNodeKind - { - Invalid, // in case the Kind field is not initialized properly - - Unknown, // unknown value, has StaticType from context - - Null, // known value - SystemType, // known value - TypeRepresented - RuntimeTypeHandle, // known value - TypeRepresented - KnownString, // known value - Contents - ConstInt, // known value - Int32 - AnnotatedString, // string with known annotation - - MethodParameter, // symbolic placeholder - MethodReturn, // symbolic placeholder - - RuntimeMethodHandle, // known value - MethodRepresented - SystemReflectionMethodBase, // known value - MethodRepresented - - RuntimeTypeHandleForGenericParameter, // symbolic placeholder for generic parameter - SystemTypeForGenericParameter, // symbolic placeholder for generic parameter - - MergePoint, // structural, multiplexer - Values - GetTypeFromString, // structural, could be known value - KnownString - Array, // structural, could be known value - Array - - LoadField, // structural, could be known value - InstanceValue - } - - /// - /// A ValueNode represents a value in the IL dataflow analysis. It may not contain complete information as it is a - /// best-effort representation. Additionally, as the analysis is linear and does not account for control flow, any - /// given ValueNode may represent multiple values simultaneously. (This occurs, for example, at control flow join - /// points when both paths yield values on the IL stack or in a local.) - /// - public abstract class ValueNode : IEquatable - { - public ValueNode() - { -#if false // Helpful for debugging a cycle that has inadvertently crept into the graph - if (this.DetectCycle(new HashSet())) - { - throw new Exception("Found a cycle"); - } -#endif - } - - /// - /// The 'kind' of value node -- this represents the most-derived type and allows us to switch over and do - /// equality checks without the cost of casting. Intermediate non-leaf types in the ValueNode hierarchy should - /// be abstract. - /// - public ValueNodeKind Kind { get; protected set; } - - /// - /// The IL type of the value, represented as closely as possible, but not always exact. It can be null, for - /// example, when the analysis is imprecise or operating on malformed IL. - /// - public TypeDesc StaticType { get; protected set; } - - /// - /// Allows the enumeration of the direct children of this node. The ChildCollection struct returned here - /// supports 'foreach' without allocation. - /// - public ChildCollection Children { get { return new ChildCollection(this); } } - - /// - /// This property allows you to enumerate all 'unique values' represented by a given ValueNode. The basic idea - /// is that there will be no MergePointValues in the returned ValueNodes and all structural operations will be - /// applied so that each 'unique value' can be considered on its own without regard to the structure that led to - /// it. - /// - public UniqueValueCollection UniqueValuesInternal - { - get - { - return new UniqueValueCollection(this); - } - } - - /// - /// This protected method is how nodes implement the UniqueValues property. It is protected because it returns - /// an IEnumerable and we want to avoid allocating an enumerator for the exceedingly common case of there being - /// only one value in the enumeration. The UniqueValueCollection returned by the UniqueValues property handles - /// this detail. - /// - protected abstract IEnumerable EvaluateUniqueValues(); - - /// - /// RepresentsExactlyOneValue is used by the UniqueValues property to allow us to bypass allocating an - /// enumerator to return just one value. If a node returns 'true' from RepresentsExactlyOneValue, it must also - /// return that one value from GetSingleUniqueValue. If it always returns 'false', it doesn't need to implement - /// GetSingleUniqueValue. - /// - protected virtual bool RepresentsExactlyOneValue { get { return false; } } - - /// - /// GetSingleUniqueValue is called if, and only if, RepresentsExactlyOneValue returns true. It allows us to - /// bypass the allocation of an enumerator for the common case of returning exactly one value. - /// - protected virtual ValueNode GetSingleUniqueValue() - { - // Not implemented because RepresentsExactlyOneValue returns false and, therefore, this method should be - // unreachable. - throw new NotImplementedException(); - } - - protected abstract int NumChildren { get; } - protected abstract ValueNode ChildAt(int index); - - public virtual bool Equals(ValueNode other) - { - return other != null && this.Kind == other.Kind && this.StaticType == other.StaticType; - } - - public abstract override int GetHashCode(); - - /// - /// Each node type must implement this to stringize itself. The expectation is that it is implemented using - /// ValueNodeDump.ValueNodeToString(), passing any non-ValueNode properties of interest (e.g. - /// SystemTypeValue.TypeRepresented). Properties that are invariant on a particular node type - /// should be omitted for clarity. - /// - protected abstract string NodeToString(); - - public override string ToString() - { - return NodeToString(); - } - - public override bool Equals(object other) - { - if (!(other is ValueNode)) - return false; - - return this.Equals((ValueNode)other); - } - - #region Specialized Collection Nested Types - /// - /// ChildCollection struct is used to wrap the operations on a node involving its children. In particular, the - /// struct implements a GetEnumerator method that is used to allow "foreach (ValueNode node in myNode.Children)" - /// without heap allocations. - /// - public struct ChildCollection : IEnumerable - { - /// - /// Enumerator for children of a ValueNode. Allows foreach(var child in node.Children) to work without - /// allocating a heap-based enumerator. - /// - public struct Enumerator : IEnumerator - { - int _index; - readonly ValueNode _parent; - - public Enumerator(ValueNode parent) - { - _parent = parent; - _index = -1; - } - - public ValueNode Current { get { return _parent?.ChildAt(_index); } } - - object System.Collections.IEnumerator.Current { get { return Current; } } - - public bool MoveNext() - { - _index++; - return (_parent != null) ? (_index < _parent.NumChildren) : false; - } - - public void Reset() - { - _index = -1; - } - - public void Dispose() - { - } - } - - readonly ValueNode _parentNode; - - public ChildCollection(ValueNode parentNode) { _parentNode = parentNode; } - - // Used by C# 'foreach', when strongly typed, to avoid allocation. - public Enumerator GetEnumerator() - { - return new Enumerator(_parentNode); - } - - IEnumerator IEnumerable.GetEnumerator() - { - // note the boxing! - return new Enumerator(_parentNode); - } - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - // note the boxing! - return new Enumerator(_parentNode); - } - - public int Count { get { return (_parentNode != null) ? _parentNode.NumChildren : 0; } } - } - - /// - /// UniqueValueCollection is used to wrap calls to ValueNode.EvaluateUniqueValues. If a ValueNode represents - /// only one value, then foreach(ValueNode value in node.UniqueValues) will not allocate a heap-based enumerator. - /// - /// This is implented by having each ValueNode tell us whether or not it represents exactly one value or not. - /// If it does, we fetch it with ValueNode.GetSingleUniqueValue(), otherwise, we fall back to the usual heap- - /// based IEnumerable returned by ValueNode.EvaluateUniqueValues. - /// - public struct UniqueValueCollection : IEnumerable - { - readonly IEnumerable _multiValueEnumerable; - readonly ValueNode _treeNode; - - public UniqueValueCollection(ValueNode node) - { - if (node.RepresentsExactlyOneValue) - { - _multiValueEnumerable = null; - _treeNode = node; - } - else - { - _multiValueEnumerable = node.EvaluateUniqueValues(); - _treeNode = null; - } - } - - public Enumerator GetEnumerator() - { - return new Enumerator(_treeNode, _multiValueEnumerable); - } - - IEnumerator IEnumerable.GetEnumerator() - { - if (_multiValueEnumerable != null) - { - return _multiValueEnumerable.GetEnumerator(); - } - - // note the boxing! - return GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - if (_multiValueEnumerable != null) - { - return _multiValueEnumerable.GetEnumerator(); - } - - // note the boxing! - return GetEnumerator(); - } - - - public struct Enumerator : IEnumerator - { - readonly IEnumerator _multiValueEnumerator; - readonly ValueNode _singleValueNode; - int _index; - - public Enumerator(ValueNode treeNode, IEnumerable mulitValueEnumerable) - { - _singleValueNode = treeNode?.GetSingleUniqueValue(); - _multiValueEnumerator = mulitValueEnumerable?.GetEnumerator(); - _index = -1; - } - - public void Reset() - { - if (_multiValueEnumerator != null) - { - _multiValueEnumerator.Reset(); - return; - } - - _index = -1; - } - - public bool MoveNext() - { - if (_multiValueEnumerator != null) - return _multiValueEnumerator.MoveNext(); - - _index++; - return _index == 0; - } - - public ValueNode Current - { - get - { - if (_multiValueEnumerator != null) - return _multiValueEnumerator.Current; - - if (_index == 0) - return _singleValueNode; - - throw new InvalidOperationException(); - } - } - - object System.Collections.IEnumerator.Current { get { return Current; } } - - public void Dispose() - { - } - } - } - #endregion - } - - /// - /// LeafValueNode represents a 'leaf' in the expression tree. In other words, the node has no ValueNode children. - /// It *may* still have non-ValueNode 'properties' that are interesting. This class serves, primarily, as a way to - /// collect up the very common implmentation of NumChildren/ChildAt for leaf nodes and the "represents exactly one - /// value" optimization. These things aren't on the ValueNode base class because, otherwise, new node types - /// deriving from ValueNode may 'forget' to implement these things. So this class allows them to remain abstract in - /// ValueNode while still having a common implementation for all the leaf nodes. - /// - public abstract class LeafValueNode : ValueNode - { - protected override int NumChildren { get { return 0; } } - protected override ValueNode ChildAt(int index) { throw new InvalidOperationException(); } - - protected override bool RepresentsExactlyOneValue { get { return true; } } - - protected override ValueNode GetSingleUniqueValue() { return this; } - - - protected override IEnumerable EvaluateUniqueValues() - { - // Leaf values should not represent more than one value. This method should be unreachable as long as - // RepresentsExactlyOneValue returns true. - throw new NotImplementedException(); - } - } - - // These are extension methods because we want to allow the use of them on null 'this' pointers. - internal static class ValueNodeExtensions - { - /// - /// Returns true if a ValueNode graph contains a cycle - /// - /// Node to evaluate - /// Set of nodes previously seen on the current arc. Callers may pass a non-empty set - /// to test whether adding that set to this node would create a cycle. Contents will be modified by the walk - /// and should not be used by the caller after returning - /// Optional. The set of all nodes encountered during a walk after DetectCycle returns - /// - public static bool DetectCycle(this ValueNode node, HashSet seenNodes, HashSet allNodesSeen) - { - if (node == null) - return false; - - if (seenNodes.Contains(node)) - return true; - - seenNodes.Add(node); - - if (allNodesSeen != null) - { - allNodesSeen.Add(node); - } - - bool foundCycle = false; - switch (node.Kind) - { - // - // Leaf nodes - // - case ValueNodeKind.Unknown: - case ValueNodeKind.Null: - case ValueNodeKind.SystemType: - case ValueNodeKind.RuntimeTypeHandle: - case ValueNodeKind.KnownString: - case ValueNodeKind.AnnotatedString: - case ValueNodeKind.ConstInt: - case ValueNodeKind.MethodParameter: - case ValueNodeKind.MethodReturn: - case ValueNodeKind.SystemTypeForGenericParameter: - case ValueNodeKind.RuntimeTypeHandleForGenericParameter: - case ValueNodeKind.SystemReflectionMethodBase: - case ValueNodeKind.RuntimeMethodHandle: - case ValueNodeKind.LoadField: - break; - - // - // Nodes with children - // - case ValueNodeKind.MergePoint: - foreach (ValueNode val in ((MergePointValue)node).Values) - { - if (val.DetectCycle(seenNodes, allNodesSeen)) - { - foundCycle = true; - } - } - break; - - case ValueNodeKind.GetTypeFromString: - GetTypeFromStringValue gtfsv = (GetTypeFromStringValue)node; - foundCycle = gtfsv.AssemblyIdentity.DetectCycle(seenNodes, allNodesSeen); - foundCycle |= gtfsv.NameString.DetectCycle(seenNodes, allNodesSeen); - break; - - case ValueNodeKind.Array: - ArrayValue av = (ArrayValue)node; - foundCycle = av.Size.DetectCycle(seenNodes, allNodesSeen); - foreach (ValueBasicBlockPair pair in av.IndexValues.Values) - { - foundCycle |= pair.Value.DetectCycle(seenNodes, allNodesSeen); - } - break; - - default: - throw new Exception(String.Format("Unknown node kind: {0}", node.Kind)); - } - seenNodes.Remove(node); - - return foundCycle; - } - - public static ValueNode.UniqueValueCollection UniqueValues(this ValueNode node) - { - if (node == null) - return new ValueNode.UniqueValueCollection(UnknownValue.Instance); - - return node.UniqueValuesInternal; - } - - public static int? AsConstInt(this ValueNode node) - { - if (node is ConstIntValue constInt) - return constInt.Value; - return null; - } - } - - internal static class ValueNodeDump - { - internal static string ValueNodeToString(ValueNode node, params object[] args) - { - if (node == null) - return ""; - - StringBuilder sb = new StringBuilder(); - sb.Append(node.Kind.ToString()); - sb.Append("("); - if (args != null) - { - for (int i = 0; i < args.Length; i++) - { - if (i > 0) - sb.Append(","); - sb.Append(args[i] == null ? "" : args[i].ToString()); - } - } - sb.Append(")"); - return sb.ToString(); - } - - static string GetIndent(int level) - { - StringBuilder sb = new StringBuilder(level * 2); - for (int i = 0; i < level; i++) - sb.Append(" "); - return sb.ToString(); - } - - public static void DumpTree(this ValueNode node, System.IO.TextWriter writer = null, int indentLevel = 0) - { - if (writer == null) - writer = Console.Out; - - writer.Write(GetIndent(indentLevel)); - if (node == null) - { - writer.WriteLine(""); - return; - } - - writer.WriteLine(node); - foreach (ValueNode child in node.Children) - { - child.DumpTree(writer, indentLevel + 1); - } - } - } - - /// - /// Represents an unknown value. - /// - class UnknownValue : LeafValueNode - { - private UnknownValue() - { - Kind = ValueNodeKind.Unknown; - StaticType = null; - } - - public static UnknownValue Instance { get; } = new UnknownValue(); - - public override bool Equals(ValueNode other) - { - return base.Equals(other); - } - - public override int GetHashCode() - { - // All instances of UnknownValue are equivalent, so they all hash to the same hashcode. This one was - // chosen for no particular reason at all. - return 0x98052; - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this); - } - } - - class NullValue : LeafValueNode - { - private NullValue() - { - Kind = ValueNodeKind.Null; - StaticType = null; - } - - public override bool Equals(ValueNode other) - { - return base.Equals(other); - } - - public static NullValue Instance { get; } = new NullValue(); - - public override int GetHashCode() - { - // All instances of NullValue are equivalent, so they all hash to the same hashcode. This one was - // chosen for no particular reason at all. - return 0x90210; - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this); - } - } - - /// - /// This is a known System.Type value. TypeRepresented is the 'value' of the System.Type. - /// - class SystemTypeValue : LeafValueNode - { - public SystemTypeValue(TypeDesc typeRepresented) - { - Kind = ValueNodeKind.SystemType; - - // Should be System.Type - but we don't have any use case where tracking it like that would matter - StaticType = null; - - TypeRepresented = typeRepresented; - } - - public TypeDesc TypeRepresented { get; private set; } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - - return Equals(this.TypeRepresented, ((SystemTypeValue)other).TypeRepresented); - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, TypeRepresented); - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this, TypeRepresented); - } - } - - /// - /// This is the System.RuntimeTypeHandle equivalent to a node. - /// - class RuntimeTypeHandleValue : LeafValueNode - { - public RuntimeTypeHandleValue(TypeDesc typeRepresented) - { - Kind = ValueNodeKind.RuntimeTypeHandle; - - // Should be System.RuntimeTypeHandle, but we don't have a use case for it like that - StaticType = null; - - TypeRepresented = typeRepresented; - } - - public TypeDesc TypeRepresented { get; } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - - return Equals(this.TypeRepresented, ((RuntimeTypeHandleValue)other).TypeRepresented); - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, TypeRepresented); - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this, TypeRepresented); - } - } - - /// - /// This is a System.Type value which represents generic parameter (basically result of typeof(T)) - /// Its actual type is unknown, but it can have annotations. - /// - class SystemTypeForGenericParameterValue : LeafValueWithDynamicallyAccessedMemberNode - { - public SystemTypeForGenericParameterValue(GenericParameterDesc genericParameter, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - { - Kind = ValueNodeKind.SystemTypeForGenericParameter; - - // Should be System.Type, but we don't have a use case for it - StaticType = null; - - GenericParameter = genericParameter; - DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; - SourceContext = new GenericParameterOrigin(genericParameter); - } - - public GenericParameterDesc GenericParameter { get; } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - - var otherValue = (SystemTypeForGenericParameterValue)other; - return this.GenericParameter == otherValue.GenericParameter && this.DynamicallyAccessedMemberTypes == otherValue.DynamicallyAccessedMemberTypes; - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, GenericParameter, DynamicallyAccessedMemberTypes); - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this, GenericParameter, DynamicallyAccessedMemberTypes); - } - } - - /// - /// This is the System.RuntimeTypeHandle equivalent to a node. - /// - class RuntimeTypeHandleForGenericParameterValue : LeafValueNode - { - public RuntimeTypeHandleForGenericParameterValue(GenericParameterDesc genericParameter) - { - Kind = ValueNodeKind.RuntimeTypeHandleForGenericParameter; - - // Should be System.RuntimeTypeHandle, but we don't have a use case for it - StaticType = null; - - GenericParameter = genericParameter; - } - - public GenericParameterDesc GenericParameter { get; } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - - return Equals(this.GenericParameter, ((RuntimeTypeHandleForGenericParameterValue)other).GenericParameter); - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, GenericParameter); - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this, GenericParameter); - } - } - - /// - /// This is the System.RuntimeMethodHandle equivalent to a node. - /// - class RuntimeMethodHandleValue : LeafValueNode - { - public RuntimeMethodHandleValue(MethodDesc methodRepresented) - { - Kind = ValueNodeKind.RuntimeMethodHandle; - - // Should be System.RuntimeMethodHandle, but we don't have a use case for it - StaticType = null; - - MethodRepresented = methodRepresented; - } - - public MethodDesc MethodRepresented { get; } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - - return Equals(this.MethodRepresented, ((RuntimeMethodHandleValue)other).MethodRepresented); - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, MethodRepresented); - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this, MethodRepresented); - } - } - - /// - /// This is a known System.Reflection.MethodBase value. MethodRepresented is the 'value' of the MethodBase. - /// - class SystemReflectionMethodBaseValue : LeafValueNode - { - public SystemReflectionMethodBaseValue(MethodDesc methodRepresented) - { - Kind = ValueNodeKind.SystemReflectionMethodBase; - - // Should be System.Reflection.MethodBase, but we don't have a use case for it - StaticType = null; - - MethodRepresented = methodRepresented; - } - - public MethodDesc MethodRepresented { get; private set; } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - - return Equals(this.MethodRepresented, ((SystemReflectionMethodBaseValue)other).MethodRepresented); - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, MethodRepresented); - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this, MethodRepresented); - } - } - - /// - /// A known string - such as the result of a ldstr. - /// - class KnownStringValue : LeafValueNode - { - public KnownStringValue(string contents) - { - Kind = ValueNodeKind.KnownString; - - // Should be System.String, but we don't have a use case for it - StaticType = null; - - Contents = contents; - } - - public string Contents { get; private set; } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - - return this.Contents == ((KnownStringValue)other).Contents; - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, Contents); - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this, "\"" + Contents + "\""); - } - } - - /// - /// Base class for all nodes which can have dynamically accessed member annotation. - /// - abstract class LeafValueWithDynamicallyAccessedMemberNode : LeafValueNode - { - public Origin SourceContext { get; protected set; } - - /// - /// The bitfield of dynamically accessed member types the node guarantees - /// - public DynamicallyAccessedMemberTypes DynamicallyAccessedMemberTypes { get; protected set; } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - var otherValue = (LeafValueWithDynamicallyAccessedMemberNode)other; - return SourceContext == otherValue.SourceContext - && DynamicallyAccessedMemberTypes == otherValue.DynamicallyAccessedMemberTypes; - } - } - - /// - /// A value that came from a method parameter - such as the result of a ldarg. - /// - class MethodParameterValue : LeafValueWithDynamicallyAccessedMemberNode - { - public MethodParameterValue(MethodDesc method, int parameterIndex, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - { - Kind = ValueNodeKind.MethodParameter; - StaticType = !method.Signature.IsStatic - ? (parameterIndex == 0 - ? method.OwningType - : method.Signature[parameterIndex - 1]) - : method.Signature[parameterIndex]; - ParameterIndex = parameterIndex; - DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; - SourceContext = !method.Signature.IsStatic && parameterIndex == 0 ? - new MethodOrigin(method) : - new ParameterOrigin(method, parameterIndex); - } - - public int ParameterIndex { get; } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - - var otherValue = (MethodParameterValue)other; - return this.ParameterIndex == otherValue.ParameterIndex; - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, ParameterIndex, DynamicallyAccessedMemberTypes); - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this, ParameterIndex, DynamicallyAccessedMemberTypes); - } - } - - /// - /// String with a known annotation. - /// - class AnnotatedStringValue : LeafValueWithDynamicallyAccessedMemberNode - { - public AnnotatedStringValue(Origin sourceContext, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - { - Kind = ValueNodeKind.AnnotatedString; - - // Should be System.String, but we don't have a use case for it - StaticType = null; - - DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; - SourceContext = sourceContext; - } - - public override bool Equals(ValueNode other) - { - return base.Equals(other); - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, DynamicallyAccessedMemberTypes); - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this, DynamicallyAccessedMemberTypes); - } - } - - /// - /// Return value from a method - /// - class MethodReturnValue : LeafValueWithDynamicallyAccessedMemberNode - { - public MethodReturnValue(MethodDesc method, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - { - Kind = ValueNodeKind.MethodReturn; - StaticType = method.Signature.ReturnType; - DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; - SourceContext = new MethodReturnOrigin(method); - } - - public override bool Equals(ValueNode other) - { - return base.Equals(other); - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, DynamicallyAccessedMemberTypes); - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this, DynamicallyAccessedMemberTypes); - } - } - - /// - /// A merge point commonly occurs due to control flow in a method body. It represents a set of values - /// from different paths through the method. It is the reason for EvaluateUniqueValues, which essentially - /// provides an enumeration over all the concrete values represented by a given ValueNode after 'erasing' - /// the merge point nodes. - /// - class MergePointValue : ValueNode - { - private MergePointValue(ValueNode one, ValueNode two) - { - Kind = ValueNodeKind.MergePoint; - StaticType = null; - m_values = new ValueNodeHashSet(); - - if (one.Kind == ValueNodeKind.MergePoint) - { - MergePointValue mpvOne = (MergePointValue)one; - foreach (ValueNode value in mpvOne.Values) - m_values.Add(value); - } - else - m_values.Add(one); - - if (two.Kind == ValueNodeKind.MergePoint) - { - MergePointValue mpvTwo = (MergePointValue)two; - foreach (ValueNode value in mpvTwo.Values) - m_values.Add(value); - } - else - m_values.Add(two); - } - - public MergePointValue() - { - Kind = ValueNodeKind.MergePoint; - m_values = new ValueNodeHashSet(); - } - - public void AddValue(ValueNode node) - { - // we are mutating our state, so we must invalidate any cached knowledge - //InvalidateIsOpen (); - - if (node.Kind == ValueNodeKind.MergePoint) - { - foreach (ValueNode value in ((MergePointValue)node).Values) - m_values.Add(value); - } - else - m_values.Add(node); - -#if false - if (this.DetectCycle(new HashSet())) - { - throw new Exception("Found a cycle"); - } -#endif - } - - readonly ValueNodeHashSet m_values; - - public ValueNodeHashSet Values { get { return m_values; } } - - protected override int NumChildren { get { return Values.Count; } } - protected override ValueNode ChildAt(int index) - { - if (index < NumChildren) - return Values.ElementAt(index); - throw new InvalidOperationException(); - } - - public static ValueNode MergeValues(ValueNode one, ValueNode two) - { - if (one == null) - return two; - else if (two == null) - return one; - else if (one.Equals(two)) - return one; - else - return new MergePointValue(one, two); - } - - protected override IEnumerable EvaluateUniqueValues() - { - foreach (ValueNode value in Values) - { - foreach (ValueNode uniqueValue in value.UniqueValuesInternal) - { - yield return uniqueValue; - } - } - } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - - MergePointValue otherMpv = (MergePointValue)other; - if (this.Values.Count != otherMpv.Values.Count) - return false; - - foreach (ValueNode value in this.Values) - { - if (!otherMpv.Values.Contains(value)) - return false; - } - return true; - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, Values); - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this); - } - } - - delegate TypeDesc TypeResolver(string assemblyString, string typeString); - - /// - /// The result of a Type.GetType. - /// AssemblyIdentity is the scope in which to resolve if the type name string is not assembly-qualified. - /// - -#pragma warning disable CA1812 // GetTypeFromStringValue is never instantiated - class GetTypeFromStringValue : ValueNode - { - private readonly TypeResolver _resolver; - - public GetTypeFromStringValue(TypeResolver resolver, ValueNode assemblyIdentity, ValueNode nameString) - { - _resolver = resolver; - Kind = ValueNodeKind.GetTypeFromString; - - // Should be System.Type, but we don't have a use case for it - StaticType = null; - - AssemblyIdentity = assemblyIdentity; - NameString = nameString; - } - - public ValueNode AssemblyIdentity { get; private set; } - - public ValueNode NameString { get; private set; } - - protected override int NumChildren { get { return 2; } } - protected override ValueNode ChildAt(int index) - { - if (index == 0) return AssemblyIdentity; - if (index == 1) return NameString; - throw new InvalidOperationException(); - } - - protected override IEnumerable EvaluateUniqueValues() - { - HashSet names = null; - - foreach (ValueNode nameStringValue in NameString.UniqueValuesInternal) - { - if (nameStringValue.Kind == ValueNodeKind.KnownString) - { - if (names == null) - { - names = new HashSet(); - } - - string typeName = ((KnownStringValue)nameStringValue).Contents; - names.Add(typeName); - } - } - - bool foundAtLeastOne = false; - - if (names != null) - { - foreach (ValueNode assemblyValue in AssemblyIdentity.UniqueValuesInternal) - { - if (assemblyValue.Kind == ValueNodeKind.KnownString) - { - string assemblyName = ((KnownStringValue)assemblyValue).Contents; - - foreach (string name in names) - { - TypeDesc typeDefinition = _resolver(assemblyName, name); - if (typeDefinition != null) - { - foundAtLeastOne = true; - yield return new SystemTypeValue(typeDefinition); - } - } - } - } - } - - if (!foundAtLeastOne) - yield return UnknownValue.Instance; - } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - - GetTypeFromStringValue otherGtfs = (GetTypeFromStringValue)other; - - return this.AssemblyIdentity.Equals(otherGtfs.AssemblyIdentity) && - this.NameString.Equals(otherGtfs.NameString); - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, AssemblyIdentity, NameString); - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this, NameString); - } - } - - /// - /// A representation of a ldfld. Note that we don't have a representation of objects containing fields - /// so there isn't much that can be done with this node type yet. - /// - class LoadFieldValue : LeafValueWithDynamicallyAccessedMemberNode - { - public LoadFieldValue(FieldDesc fieldToLoad, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes) - { - Kind = ValueNodeKind.LoadField; - StaticType = fieldToLoad.FieldType; - Field = fieldToLoad; - DynamicallyAccessedMemberTypes = dynamicallyAccessedMemberTypes; - SourceContext = new FieldOrigin(fieldToLoad); - } - - public FieldDesc Field { get; private set; } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - - LoadFieldValue otherLfv = (LoadFieldValue)other; - return Equals(this.Field, otherLfv.Field); - } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, Field, DynamicallyAccessedMemberTypes); - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this, Field, DynamicallyAccessedMemberTypes); - } - } - - /// - /// Represents a ldc on an int32. - /// - class ConstIntValue : LeafValueNode - { - public ConstIntValue(int value) - { - Kind = ValueNodeKind.ConstInt; - - // Should be System.Int32, but we don't have a usecase for it right now - StaticType = null; - - Value = value; - } - - public int Value { get; private set; } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, Value); - } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - - ConstIntValue otherCiv = (ConstIntValue)other; - return Value == otherCiv.Value; - } - - protected override string NodeToString() - { - return ValueNodeDump.ValueNodeToString(this, Value); - } - } - - class ArrayValue : ValueNode - { - protected override int NumChildren => 1 + IndexValues.Count; - - /// - /// Constructs an array value of the given size - /// - public ArrayValue(ValueNode size, TypeDesc elementType) - { - Kind = ValueNodeKind.Array; - // Should be System.Array (or similar), but we don't have a use case for it - StaticType = null; - Size = size ?? UnknownValue.Instance; - ElementType = elementType; - IndexValues = new Dictionary(); - } - private ArrayValue(ValueNode size, TypeDesc elementType, Dictionary indexValues) - : this(size, elementType) - { - IndexValues = indexValues; - } - - public ValueNode Size { get; } - public TypeDesc ElementType { get; } - public Dictionary IndexValues { get; } - - public override int GetHashCode() - { - return HashCode.Combine(Kind, Size); - } - - public override bool Equals(ValueNode other) - { - if (!base.Equals(other)) - return false; - - ArrayValue otherArr = (ArrayValue)other; - bool equals = Size.Equals(otherArr.Size); - equals &= IndexValues.Count == otherArr.IndexValues.Count; - if (!equals) - return false; - // If both sets T and O are the same size and "T intersect O" is empty, then T == O. - HashSet> thisValueSet = new(IndexValues); - HashSet> otherValueSet = new(otherArr.IndexValues); - thisValueSet.ExceptWith(otherValueSet); - return thisValueSet.Count == 0; - } - - protected override string NodeToString() - { - // TODO: Use StringBuilder and remove Linq usage. - return $"(Array Size:{ValueNodeDump.ValueNodeToString(this, Size)}, Values:({string.Join(',', IndexValues.Select(v => $"({v.Key},{ValueNodeDump.ValueNodeToString(v.Value.Value)})"))})"; - } - - protected override IEnumerable EvaluateUniqueValues() - { - foreach (var sizeConst in Size.UniqueValuesInternal) - yield return new ArrayValue(sizeConst, ElementType, IndexValues); - } - - protected override ValueNode ChildAt(int index) - { - if (index == 0) return Size; - if (index - 1 <= IndexValues.Count) - return IndexValues.Values.ElementAt(index - 1).Value; - - throw new InvalidOperationException(); - } - } - - #region ValueNode Collections - public class ValueNodeList : List + public class ValueNodeList : List { public ValueNodeList() { @@ -1332,17 +21,20 @@ public ValueNodeList(int capacity) { } - public ValueNodeList(List other) + public ValueNodeList(List other) : base(other) { } public override int GetHashCode() { - return HashUtils.CalcHashCodeEnumerable(this); + HashCode hashCode = new HashCode(); + foreach (var item in this) + hashCode.Add(item.GetHashCode()); + return hashCode.ToHashCode(); } - public override bool Equals(object other) + public override bool Equals(object? other) { if (!(other is ValueNodeList otherList)) return false; @@ -1352,57 +44,22 @@ public override bool Equals(object other) for (int i = 0; i < Count; i++) { - if (!(otherList[i]?.Equals(this[i]) ?? (this[i] is null))) - return false; - } - return true; - } - } - - class ValueNodeHashSet : HashSet - { - public override int GetHashCode() - { - return HashUtils.CalcHashCodeEnumerable(this); - } - - public override bool Equals(object other) - { - if (!(other is ValueNodeHashSet otherSet)) - return false; - - if (otherSet.Count != Count) - return false; - - IEnumerator thisEnumerator = this.GetEnumerator(); - IEnumerator otherEnumerator = otherSet.GetEnumerator(); - - for (int i = 0; i < Count; i++) - { - thisEnumerator.MoveNext(); - otherEnumerator.MoveNext(); - if (!thisEnumerator.Current.Equals(otherEnumerator.Current)) + if (!otherList[i].Equals(this[i])) return false; } return true; } } - #endregion - static class HashUtils + public struct ValueBasicBlockPair { - public static int CalcHashCodeEnumerable(IEnumerable list) where T : class + public ValueBasicBlockPair(MultiValue value, int basicBlockIndex) { - HashCode hashCode = new HashCode(); - foreach (var item in list) - hashCode.Add(item); - return hashCode.ToHashCode(); + Value = value; + BasicBlockIndex = basicBlockIndex; } - } - public struct ValueBasicBlockPair - { - public ValueNode Value; - public int BasicBlockIndex; + public MultiValue Value { get; } + public int BasicBlockIndex { get; } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs index 7efb6460e10e9..07a9e0842570b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs @@ -87,49 +87,13 @@ public void LogWarning(TypeSystemEntity origin, DiagnosticId id, params string[] public void LogWarning(string text, int code, MethodIL origin, int ilOffset, string subcategory = MessageSubCategory.None) { - string document = null; - int? lineNumber = null; - - IEnumerable sequencePoints = origin.GetDebugInfo()?.GetSequencePoints(); - if (sequencePoints != null) - { - foreach (var sequencePoint in sequencePoints) - { - if (sequencePoint.Offset <= ilOffset) - { - document = sequencePoint.Document; - lineNumber = sequencePoint.LineNumber; - } - } - } - - MethodDesc warnedMethod = CompilerGeneratedState.GetUserDefinedMethodForCompilerGeneratedMember(origin.OwningMethod) ?? origin.OwningMethod; - - MessageOrigin messageOrigin = new MessageOrigin(warnedMethod, document, lineNumber, null); + MessageOrigin messageOrigin = new MessageOrigin(origin, ilOffset); LogWarning(text, code, messageOrigin, subcategory); } public void LogWarning(MethodIL origin, int ilOffset, DiagnosticId id, params string[] args) { - string document = null; - int? lineNumber = null; - - IEnumerable sequencePoints = origin.GetDebugInfo()?.GetSequencePoints(); - if (sequencePoints != null) - { - foreach (var sequencePoint in sequencePoints) - { - if (sequencePoint.Offset <= ilOffset) - { - document = sequencePoint.Document; - lineNumber = sequencePoint.LineNumber; - } - } - } - - MethodDesc warnedMethod = CompilerGeneratedState.GetUserDefinedMethodForCompilerGeneratedMember(origin.OwningMethod) ?? origin.OwningMethod; - - MessageOrigin messageOrigin = new MessageOrigin(warnedMethod, document, lineNumber, null); + MessageOrigin messageOrigin = new MessageOrigin(origin, ilOffset); LogWarning(messageOrigin, id, args); } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index 4d1f784b22452..c5b5a458afbd3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -17,7 +17,7 @@ using ILCompiler.DependencyAnalysisFramework; using ILLink.Shared; -using FlowAnnotations = ILCompiler.Dataflow.FlowAnnotations; +using FlowAnnotations = ILLink.Shared.TrimAnalysis.FlowAnnotations; using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyList; using CombinedDependencyList = System.Collections.Generic.List.CombinedDependencyListEntry>; using Debug = System.Diagnostics.Debug; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 754a420618262..6f19f9f7acc73 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -314,16 +314,29 @@ + + + + + + + + + + + - + + + diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 647ee1f7a9eeb..925fd5c9a90d8 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -771,7 +771,7 @@ static string ILLinkify(string rootedAssembly) DynamicInvokeThunkGenerationPolicy invokeThunkGenerationPolicy = new DefaultDynamicInvokeThunkGenerationPolicy(); - var flowAnnotations = new Dataflow.FlowAnnotations(logger, ilProvider); + var flowAnnotations = new ILLink.Shared.TrimAnalysis.FlowAnnotations(logger, ilProvider); MetadataManager metadataManager = new UsageBasedMetadataManager( compilationGroup, diff --git a/src/coreclr/tools/aot/ILLink.Shared/Annotations.cs b/src/coreclr/tools/aot/ILLink.Shared/Annotations.cs new file mode 100644 index 0000000000000..ca09218c0cc9c --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/Annotations.cs @@ -0,0 +1,138 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using ILLink.Shared.TrimAnalysis; + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +namespace ILLink.Shared +{ + // Temporary workaround - should be removed once linker can be upgraded to build against + // high enough version of the framework which has this enum value. + internal static class DynamicallyAccessedMemberTypesOverlay + { + public const DynamicallyAccessedMemberTypes Interfaces = (DynamicallyAccessedMemberTypes) 0x2000; + } + + internal static class Annotations + { + public static bool SourceHasRequiredAnnotations ( + DynamicallyAccessedMemberTypes sourceMemberTypes, + DynamicallyAccessedMemberTypes targetMemberTypes, + out string missingMemberTypesString) + { + missingMemberTypesString = string.Empty; + + var missingMemberTypes = GetMissingMemberTypes (targetMemberTypes, sourceMemberTypes); + if (missingMemberTypes == DynamicallyAccessedMemberTypes.None) + return true; + + missingMemberTypesString = GetMemberTypesString (missingMemberTypes); + return false; + } + + public static DynamicallyAccessedMemberTypes GetMissingMemberTypes (DynamicallyAccessedMemberTypes requiredMemberTypes, DynamicallyAccessedMemberTypes availableMemberTypes) + { + if (availableMemberTypes.HasFlag (requiredMemberTypes)) + return DynamicallyAccessedMemberTypes.None; + + if (requiredMemberTypes == DynamicallyAccessedMemberTypes.All) + return DynamicallyAccessedMemberTypes.All; + + var missingMemberTypes = requiredMemberTypes & ~availableMemberTypes; + + // PublicConstructors is a special case since its value is 3 - so PublicParameterlessConstructor (1) | _PublicConstructor_WithMoreThanOneParameter_ (2) + // The above bit logic only works for value with single bit set. + if (requiredMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors) && + !availableMemberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors)) + missingMemberTypes |= DynamicallyAccessedMemberTypes.PublicConstructors; + + return missingMemberTypes; + } + + public static string GetMemberTypesString (DynamicallyAccessedMemberTypes memberTypes) + { + Debug.Assert (memberTypes != DynamicallyAccessedMemberTypes.None); + + if (memberTypes == DynamicallyAccessedMemberTypes.All) + return $"'{nameof (DynamicallyAccessedMemberTypes)}.{nameof (DynamicallyAccessedMemberTypes.All)}'"; + + var memberTypesList = AllDynamicallyAccessedMemberTypes + .Where (damt => (memberTypes & damt) == damt && damt != DynamicallyAccessedMemberTypes.None) + .ToList (); + + if (memberTypes.HasFlag (DynamicallyAccessedMemberTypes.PublicConstructors)) + memberTypesList.Remove (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor); + + return string.Join (", ", memberTypesList.Select (mt => { + string mtName = mt == DynamicallyAccessedMemberTypesOverlay.Interfaces + ? nameof (DynamicallyAccessedMemberTypesOverlay.Interfaces) + : mt.ToString (); + + return $"'{nameof (DynamicallyAccessedMemberTypes)}.{mtName}'"; + })); + } + + static readonly DynamicallyAccessedMemberTypes[] AllDynamicallyAccessedMemberTypes = GetAllDynamicallyAccessedMemberTypes (); + + static DynamicallyAccessedMemberTypes[] GetAllDynamicallyAccessedMemberTypes () + { + var values = new HashSet ( + Enum.GetValues (typeof (DynamicallyAccessedMemberTypes)) + .Cast ()); + if (!values.Contains (DynamicallyAccessedMemberTypesOverlay.Interfaces)) + values.Add (DynamicallyAccessedMemberTypesOverlay.Interfaces); + return values.ToArray (); + } + + public static (DiagnosticId Id, string[] Arguments) GetDiagnosticForAnnotationMismatch (ValueWithDynamicallyAccessedMembers source, ValueWithDynamicallyAccessedMembers target, string missingAnnotations) + { + DiagnosticId diagnosticId = (source, target) switch { + (MethodParameterValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsParameter, + (MethodParameterValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsMethodReturnType, + (MethodParameterValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsField, + (MethodParameterValue, MethodThisParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsThisParameter, + (MethodParameterValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsGenericParameter, + (MethodReturnValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsParameter, + (MethodReturnValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsMethodReturnType, + (MethodReturnValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsField, + (MethodReturnValue, MethodThisParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsThisParameter, + (MethodReturnValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsGenericParameter, + (FieldValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsParameter, + (FieldValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsMethodReturnType, + (FieldValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsField, + (FieldValue, MethodThisParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsThisParameter, + (FieldValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsGenericParameter, + (MethodThisParameterValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsParameter, + (MethodThisParameterValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsMethodReturnType, + (MethodThisParameterValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsField, + (MethodThisParameterValue, MethodThisParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsThisParameter, + (MethodThisParameterValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsGenericParameter, + (GenericParameterValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsParameter, + (GenericParameterValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsMethodReturnType, + (GenericParameterValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsField, + (GenericParameterValue, MethodThisParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsThisParameter, + (GenericParameterValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsGenericParameter, + (NullableValueWithDynamicallyAccessedMembers, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsParameter, + (NullableValueWithDynamicallyAccessedMembers, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsMethodReturnType, + (NullableValueWithDynamicallyAccessedMembers, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsField, + (NullableValueWithDynamicallyAccessedMembers, MethodThisParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsThisParameter, + (NullableValueWithDynamicallyAccessedMembers, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsGenericParameter, + + _ => throw new NotImplementedException ($"Unsupported source context {source} or target context {target}.") + }; + + var args = new List (); + args.AddRange (target.GetDiagnosticArgumentsForAnnotationMismatch ()); + args.AddRange (source.GetDiagnosticArgumentsForAnnotationMismatch ()); + args.Add (missingAnnotations); + return (diagnosticId, args.ToArray ()); + } + } +} diff --git a/src/coreclr/tools/aot/ILLink.Shared/DataFlow/DefaultValueDictionary.cs b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/DefaultValueDictionary.cs new file mode 100644 index 0000000000000..679f70ca621ea --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/DefaultValueDictionary.cs @@ -0,0 +1,88 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +namespace ILLink.Shared.DataFlow +{ + // This is a dictionary along with a default value, where every possible key either maps to + // the default value, or another value. The default value is never explicitly stored in the dictionary, + // and the empty dictionary (where all possible keys have the default value) is represented without + // actually allocating a dictionary. + public struct DefaultValueDictionary : IEquatable>, + IEnumerable> + where TKey : IEquatable + where TValue : IEquatable + { + Dictionary? Dictionary; + + readonly TValue DefaultValue; + + public DefaultValueDictionary (TValue defaultValue) => (Dictionary, DefaultValue) = (null, defaultValue); + + public DefaultValueDictionary (DefaultValueDictionary other) + { + Dictionary = other.Dictionary == null ? null : new Dictionary (other.Dictionary); + DefaultValue = other.DefaultValue; + } + + public TValue Get (TKey key) => Dictionary?.TryGetValue (key, out var value) == true ? value : DefaultValue; + + public void Set (TKey key, TValue value) + { + if (value.Equals (DefaultValue)) + Dictionary?.Remove (key); + else + (Dictionary ??= new Dictionary ())[key] = value; + } + + public bool Equals (DefaultValueDictionary other) + { + if (!DefaultValue.Equals (other.DefaultValue)) + return false; + + if (Dictionary == null) + return other.Dictionary == null; + + if (other.Dictionary == null) + return false; + + if (Dictionary.Count != other.Dictionary.Count) + return false; + + foreach (var kvp in other.Dictionary) { + if (!Get (kvp.Key).Equals (kvp.Value)) + return false; + } + + return true; + } + + public IEnumerator> GetEnumerator () + { + return Dictionary?.GetEnumerator () ?? Enumerable.Empty> ().GetEnumerator (); + } + + IEnumerator IEnumerable.GetEnumerator () => GetEnumerator (); + + public override string ToString () + { + StringBuilder sb = new (); + sb.Append ("{"); + if (Dictionary != null) { + foreach (var kvp in Dictionary) + sb.Append (Environment.NewLine).Append ('\t').Append (kvp.Key.ToString ()).Append (" -> ").Append (kvp.Value.ToString ()); + } + sb.Append (Environment.NewLine).Append ("\t_ -> ").Append (DefaultValue.ToString ()); + sb.Append (Environment.NewLine).Append ("}"); + return sb.ToString (); + } + } +} diff --git a/src/coreclr/tools/aot/ILLink.Shared/DataFlow/DictionaryLattice.cs b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/DictionaryLattice.cs new file mode 100644 index 0000000000000..77732b28f4c7a --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/DictionaryLattice.cs @@ -0,0 +1,38 @@ +// 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; + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +namespace ILLink.Shared.DataFlow +{ + // A lattice over dictionaries where the stored values are also from a lattice. + public readonly struct DictionaryLattice : ILattice> + where TKey : IEquatable + where TValue : IEquatable + where TValueLattice : ILattice + { + public readonly TValueLattice ValueLattice; + + public DefaultValueDictionary Top { get; } + + public DictionaryLattice (TValueLattice valueLattice) + { + ValueLattice = valueLattice; + Top = new DefaultValueDictionary (valueLattice.Top); + } + + public DefaultValueDictionary Meet (DefaultValueDictionary left, DefaultValueDictionary right) + { + var met = new DefaultValueDictionary (left); + foreach (var kvp in right) { + TKey key = kvp.Key; + TValue rightValue = kvp.Value; + met.Set (key, ValueLattice.Meet (left.Get (key), rightValue)); + } + return met; + } + } +} diff --git a/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ForwardDataFlowAnalysis.cs b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ForwardDataFlowAnalysis.cs new file mode 100644 index 0000000000000..345fbe8680a54 --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ForwardDataFlowAnalysis.cs @@ -0,0 +1,356 @@ +// 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.Collections.Generic; +using System.Diagnostics; + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +namespace ILLink.Shared.DataFlow +{ + // A generic implementation of a forward dataflow analysis. Forward means that it flows facts + // across code in the order of execution, starting from the beginning of a method, + // and merging values from predecessors. + public abstract class ForwardDataFlowAnalysis + where TValue : struct, IEquatable + where TState : class, IDataFlowState, new() + where TLattice : ILattice + where TTransfer : ITransfer + where TBlock : IEquatable + where TRegion : IRegion + where TControlFlowGraph : IControlFlowGraph + { + + // Data structure to store dataflow states for every basic block in the control flow graph, + // keeping the exception states shared across different basic blocks owned by the same try or catch region. + struct ControlFlowGraphState + { + // Dataflow states for each basic block + readonly Dictionary blockOutput; + + // The control flow graph doesn't contain edges for exceptional control flow: + // - From any point in a try region to the start of any catch or finally + // - From any point in a catch region to the start of a finally or the end of a try-catch block + // These implicit edges are handled by tracking an auxiliary state for each try and catch region, + // which the transfer functions are expected to update (in addition to the normal state updates) + // when visiting operations inside of a try or catch region. + + // Dataflow states for exceptions propagating out of try or catch regions + readonly Dictionary> exceptionState; + + // Control may flow through a finally region when an exception is thrown from anywhere in the corresponding + // try or catch regions, or as part of non-exceptional control flow out of a try or catch. + // We track a separate finally state for the exceptional case. Only the normal (non-exceptional) state is + // propagated out of the finally. + + // Dataflow states for finally blocks when exception propagate through the finally region + readonly Dictionary exceptionFinallyState; + + // Finally regions may be reached (along non-exceptional paths) + // from multiple branches. This gets updated to track the normal finally input + // states from all of these branches (which aren't represented explicitly in the CFG). + readonly Dictionary finallyInputState; + + readonly TControlFlowGraph cfg; + readonly TLattice lattice; + + public ControlFlowGraphState (TControlFlowGraph cfg, TLattice lattice) + { + blockOutput = new (); + exceptionState = new (); + exceptionFinallyState = new (); + finallyInputState = new (); + this.cfg = cfg; + this.lattice = lattice; + } + + public Box GetExceptionState (TRegion tryOrCatchOrFilterRegion) + { + if (tryOrCatchOrFilterRegion.Kind is not (RegionKind.Try or RegionKind.Catch or RegionKind.Filter)) + throw new ArgumentException (null, nameof (tryOrCatchOrFilterRegion)); + + if (!exceptionState.TryGetValue (tryOrCatchOrFilterRegion, out Box? state)) { + state = new Box (lattice.Top); + exceptionState.Add (tryOrCatchOrFilterRegion, state); + } + return state; + } + + public bool TryGetExceptionState (TBlock block, out Box? state) + { + state = null; + if (!cfg.TryGetEnclosingTryOrCatchOrFilter (block, out TRegion? tryOrCatchOrFilterRegion)) + return false; + + state = GetExceptionState (tryOrCatchOrFilterRegion); + return true; + } + + public TValue GetFinallyInputState (TRegion finallyRegion) + { + if (finallyRegion.Kind is not RegionKind.Finally) + throw new ArgumentException (null, nameof (finallyRegion)); + + if (!finallyInputState.TryGetValue (finallyRegion, out TValue state)) { + state = lattice.Top; + finallyInputState.Add (finallyRegion, state); + } + return state; + } + + public void SetFinallyInputState (TRegion finallyRegion, TValue state) + { + if (finallyRegion.Kind is not RegionKind.Finally) + throw new ArgumentException (null, nameof (finallyRegion)); + + finallyInputState[finallyRegion] = state; + } + + public bool TryGetExceptionFinallyState (TBlock block, out TValue state) + { + state = default; + if (!cfg.TryGetEnclosingFinally (block, out _)) + return false; + + if (!exceptionFinallyState.TryGetValue (block, out state)) { + state = lattice.Top; + exceptionFinallyState.Add (block, state); + } + return true; + } + + public void SetExceptionFinallyState (TBlock block, TValue state) + { + if (!cfg.TryGetEnclosingFinally (block, out _)) + throw new InvalidOperationException (); + + exceptionFinallyState[block] = state; + } + + public TState Get (TBlock block) + { + if (!blockOutput.TryGetValue (block, out TState? state)) { + TryGetExceptionState (block, out Box? exceptionState); + state = new TState () { + Lattice = lattice, + Current = lattice.Top, + Exception = exceptionState + }; + blockOutput.Add (block, state); + } + return state; + } + } + + [Conditional ("DEBUG")] + public virtual void TraceStart (TControlFlowGraph cfg) { } + + [Conditional ("DEBUG")] + public virtual void TraceVisitBlock (TBlock block) { } + + [Conditional ("DEBUG")] + public virtual void TraceBlockInput (TValue normalState, TValue? exceptionState, TValue? exceptionFinallyState) { } + + [Conditional ("DEBUG")] + public virtual void TraceBlockOutput (TValue normalState, TValue? exceptionState, TValue? exceptionFinallyState) { } + + // This just runs a dataflow algorithm until convergence. It doesn't cache any results, + // allowing each particular kind of analysis to decide what is worth saving. + public void Fixpoint (TControlFlowGraph cfg, TLattice lattice, TTransfer transfer) + { + TraceStart (cfg); + + // Initialize output of each block to the Top value of the lattice + var cfgState = new ControlFlowGraphState (cfg, lattice); + + // For now, the actual dataflow algorithm is the simplest possible version. + // It is written to be obviously correct, but has not been optimized for performance + // at all. As written it will almost always perform unnecessary passes over the entire + // control flow graph. The core abstractions shouldn't need to change even when we write + // an optimized version of this algorithm - ideally any optimizations will be generic, + // not specific to a particular analysis. + + // Allocate some objects which will be reused to hold the current dataflow state, + // to avoid allocatons in the inner loop below. + var state = new TState () { + Lattice = lattice, + Current = lattice.Top, + Exception = null + }; + var finallyState = new TState () { + Lattice = lattice, + Current = lattice.Top, + Exception = null + }; + + bool changed = true; + while (changed) { + changed = false; + foreach (var block in cfg.Blocks) { + + TraceVisitBlock (block); + + if (block.Equals (cfg.Entry)) + continue; + + bool isTryOrCatchOrFilterBlock = cfg.TryGetEnclosingTryOrCatchOrFilter (block, out TRegion? tryOrCatchOrFilterRegion); + + bool isTryBlock = isTryOrCatchOrFilterBlock && tryOrCatchOrFilterRegion!.Kind == RegionKind.Try; + bool isTryStart = isTryBlock && block.Equals (cfg.FirstBlock (tryOrCatchOrFilterRegion!)); + bool isCatchBlock = isTryOrCatchOrFilterBlock && tryOrCatchOrFilterRegion!.Kind == RegionKind.Catch; + bool isCatchStartWithoutFilter = isCatchBlock && block.Equals (cfg.FirstBlock (tryOrCatchOrFilterRegion!)) && !cfg.HasFilter (tryOrCatchOrFilterRegion!); + bool isFilterBlock = isTryOrCatchOrFilterBlock && tryOrCatchOrFilterRegion!.Kind == RegionKind.Filter; + bool isFilterStart = isFilterBlock && block.Equals (cfg.FirstBlock (tryOrCatchOrFilterRegion!)); + + bool isCatchOrFilterStart = isCatchStartWithoutFilter || isFilterStart; + + bool isFinallyBlock = cfg.TryGetEnclosingFinally (block, out TRegion? finallyRegion); + bool isFinallyStart = isFinallyBlock && block.Equals (cfg.FirstBlock (finallyRegion!)); + + + // + // Meet over predecessors to get the new value at the start of this block. + // + + // Compute the dataflow state at the beginning of this block. + TValue currentState = lattice.Top; + foreach (var predecessor in cfg.GetPredecessors (block)) { + TValue predecessorState = cfgState.Get (predecessor.Block).Current; + + // Propagate state through all finally blocks. + foreach (var exitedFinally in predecessor.FinallyRegions) { + TValue oldFinallyInputState = cfgState.GetFinallyInputState (exitedFinally); + TValue finallyInputState = lattice.Meet (oldFinallyInputState, predecessorState); + + cfgState.SetFinallyInputState (exitedFinally, finallyInputState); + + // Note: the current approach here is inefficient for long chains of finally regions because + // the states will not converge until we have visited each block along the chain + // and propagated the new states along this path. + if (!changed && !finallyInputState.Equals (oldFinallyInputState)) + changed = true; + + TBlock lastFinallyBlock = cfg.LastBlock (exitedFinally); + predecessorState = cfgState.Get (lastFinallyBlock).Current; + } + + currentState = lattice.Meet (currentState, predecessorState); + } + // State at start of a catch also includes the exceptional state from + // try -> catch exceptional control flow. + if (isCatchOrFilterStart) { + TRegion correspondingTry = cfg.GetCorrespondingTry (tryOrCatchOrFilterRegion!); + Box tryExceptionState = cfgState.GetExceptionState (correspondingTry); + currentState = lattice.Meet (currentState, tryExceptionState.Value); + + // A catch or filter can also be reached from a previous filter. + foreach (TRegion previousFilter in cfg.GetPreviousFilters (tryOrCatchOrFilterRegion!)) { + // Control may flow from the last block of a previous filter region to this catch or filter region. + // Exceptions may also propagate from anywhere in a filter region to this catch or filter region. + // This covers both cases since the exceptional state is a superset of the normal state. + Box previousFilterExceptionState = cfgState.GetExceptionState (previousFilter); + currentState = lattice.Meet (currentState, previousFilterExceptionState.Value); + } + } + if (isFinallyStart) { + TValue finallyInputState = cfgState.GetFinallyInputState (finallyRegion!); + currentState = lattice.Meet (currentState, finallyInputState); + } + + // Compute the independent exceptional finally state at beginning of a finally. + TValue? exceptionFinallyState = null; + if (isFinallyBlock) { + // Inside finally regions, must compute the parallel meet state for unhandled exceptions. + // Using predecessors in the finally. But not from outside the finally. + exceptionFinallyState = lattice.Top; + foreach (var predecessor in cfg.GetPredecessors (block)) { + var isPredecessorInFinally = cfgState.TryGetExceptionFinallyState (predecessor.Block, out TValue predecessorFinallyState); + Debug.Assert (isPredecessorInFinally); + exceptionFinallyState = lattice.Meet (exceptionFinallyState.Value, predecessorFinallyState); + } + + // For first block, also initialize it from the try or catch blocks. + if (isFinallyStart) { + // Note that try+catch+finally is represented in the region hierarchy like a + // try/finally where the try block contains a try/catch. + // So including the corresponding try exception state will also take care of the + // corresponding catch exception state. + TRegion correspondingTry = cfg.GetCorrespondingTry (finallyRegion!); + Box tryExceptionState = cfgState.GetExceptionState (correspondingTry); + exceptionFinallyState = lattice.Meet (exceptionFinallyState.Value, tryExceptionState.Value); + } + } + + // Initialize the exception state at the start of try/catch regions. Control flow edges from predecessors + // within the same try or catch region don't need to be handled here because the transfer functions update + // the exception state to reflect every operation in the region. + TState currentBlockState = cfgState.Get (block); + Box? exceptionState = currentBlockState.Exception; + TValue? oldExceptionState = exceptionState?.Value; + if (isTryStart || isCatchOrFilterStart) { + // Catch/filter regions get the initial state from the exception state of the corresponding try region. + // This is already accounted for in the non-exceptional control flow state of the catch block above, + // so we can just use the state we already computed, for both try and catch regions. + exceptionState!.Value = lattice.Meet (exceptionState!.Value, currentState); + + if (isFinallyBlock) { + // Exceptions could also be thrown from inside a finally that was entered due to a previous exception. + // So the exception state must also include values from the exceptional finally state (computed above). + exceptionState!.Value = lattice.Meet (exceptionState!.Value, exceptionFinallyState!.Value); + } + } + + TraceBlockInput (currentState, exceptionState?.Value, exceptionFinallyState); + + // + // Apply transfer functions to the met input to get an output value for this block. + // + + state.Current = currentState; + state.Exception = exceptionState; + transfer.Transfer (block, state); + + if (!changed && !cfgState.Get (block).Current.Equals (state.Current)) + changed = true; + + cfgState.Get (block).Current = state.Current; + Debug.Assert (cfgState.Get (block).Exception == state.Exception); + + if (cfgState.TryGetExceptionFinallyState (block, out TValue oldFinallyState)) { + // Independently apply transfer functions for the exception finally state in finally regions. + finallyState.Current = exceptionFinallyState!.Value; + finallyState.Exception = exceptionState; + transfer.Transfer (block, finallyState); + + if (!changed && !oldFinallyState.Equals (finallyState.Current)) + changed = true; + + Debug.Assert (cfgState.Get (block).Exception == state.Exception); + cfgState.SetExceptionFinallyState (block, finallyState.Current); + } + + // Either the normal transfer or the finally transfer might change + // the try/catch state, so this check should happen after both transfers. + if (exceptionState?.Value.Equals (oldExceptionState!.Value) == false) { + Debug.Assert (exceptionState != null); + Debug.Assert (oldExceptionState != null); + changed = true; + + // Bubble up the changed exception state to the next enclosing try or catch exception state. + while (cfg.TryGetEnclosingTryOrCatchOrFilter (tryOrCatchOrFilterRegion!, out TRegion? enclosingTryOrCatch)) { + // Filters can't contain try/catch/filters. + Debug.Assert (enclosingTryOrCatch.Kind != RegionKind.Filter); + Box tryOrCatchExceptionState = cfgState.GetExceptionState (enclosingTryOrCatch); + tryOrCatchExceptionState.Value = lattice.Meet (tryOrCatchExceptionState!.Value, exceptionState!.Value); + tryOrCatchOrFilterRegion = enclosingTryOrCatch; + } + } + + TraceBlockOutput (state.Current, exceptionState?.Value, exceptionFinallyState); + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILLink.Shared/DataFlow/IControlFlowGraph.cs b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/IControlFlowGraph.cs new file mode 100644 index 0000000000000..eb1b492aa308d --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/IControlFlowGraph.cs @@ -0,0 +1,67 @@ +// 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.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +namespace ILLink.Shared.DataFlow +{ + public enum RegionKind + { + Try, + Catch, + Filter, + Finally + } + + public interface IRegion : IEquatable + { + RegionKind Kind { get; } + } + + public interface IControlFlowGraph + where TBlock : IEquatable + where TRegion : IRegion + { + + public readonly struct Predecessor + { + public readonly TBlock Block; + public readonly ImmutableArray FinallyRegions; + public Predecessor (TBlock block, ImmutableArray finallyRegions) + { + (Block, FinallyRegions) = (block, finallyRegions); + } + } + + IEnumerable Blocks { get; } + + TBlock Entry { get; } + + // This does not include predecessor edges for exceptional control flow into + // catch regions or finally regions. It also doesn't include edges for non-exceptional + // control flow from try -> finally or from catch -> finally. + IEnumerable GetPredecessors (TBlock block); + + bool TryGetEnclosingTryOrCatchOrFilter (TBlock block, [NotNullWhen (true)] out TRegion? tryOrCatchOrFilterRegion); + + bool TryGetEnclosingTryOrCatchOrFilter (TRegion region, [NotNullWhen (true)] out TRegion? tryOrCatchOrFilterRegion); + + bool TryGetEnclosingFinally (TBlock block, [NotNullWhen (true)] out TRegion? region); + + TRegion GetCorrespondingTry (TRegion cathOrFilterOrFinallyRegion); + + IEnumerable GetPreviousFilters (TRegion catchOrFilterRegion); + + bool HasFilter (TRegion catchRegion); + + TBlock FirstBlock (TRegion region); + + TBlock LastBlock (TRegion region); + } +} diff --git a/src/coreclr/tools/aot/ILLink.Shared/DataFlow/IDataFlowState.cs b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/IDataFlowState.cs new file mode 100644 index 0000000000000..26f056be835e0 --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/IDataFlowState.cs @@ -0,0 +1,26 @@ +// 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; + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +namespace ILLink.Shared.DataFlow +{ + public sealed class Box where T : struct + { + public Box (T value) => Value = value; + public T Value { get; set; } + + } + + public interface IDataFlowState + where TValue : struct, IEquatable + where TValueLattice : ILattice + { + TValue Current { get; set; } + Box? Exception { get; set; } + TValueLattice Lattice { get; init; } + } +} diff --git a/src/coreclr/tools/aot/ILLink.Shared/DataFlow/IDeepCopyValue.cs b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/IDeepCopyValue.cs new file mode 100644 index 0000000000000..da12780949526 --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/IDeepCopyValue.cs @@ -0,0 +1,14 @@ +// 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. + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +namespace ILLink.Shared.DataFlow +{ + // Adds ability to deep copy a value + public interface IDeepCopyValue + { + public TSingleValue DeepCopy (); + } +} diff --git a/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ILattice.cs b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ILattice.cs new file mode 100644 index 0000000000000..07f31c4e5ed35 --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ILattice.cs @@ -0,0 +1,42 @@ +// 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; + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +namespace ILLink.Shared.DataFlow +{ + // ILattice represents a lattice (technically a semilattice) of values. + // A semilattice is a set of values along with a meet operation (or greatest lower bound). + // The meet operation imposes a partial order on the values: a <= b iff Meet(a, b) == a. + + // In a dataflow analysis, the Meet operation is used to combine the tracked facts when + // there are multiple control flow paths that reach the same program point (or in a backwards + // analysis, to combine the tracked facts from multiple control flow paths out of a program point). + + // The interface constraint on TValue ensures that trying to instantiate + // ILattice over a nullable type will produce a warning or error. + + // The lattice might be better represented as an interface describing individual lattice values + // (as opposed to describing the lattice structure as ILattice does), with Top being a static + // virtual interface method. This would avoid the need to pass around multiple generic arguments + // (TValue and TLattice). However, we can't use static virtual interface methods in the analyzer + // so the lattice instance provides the Top value. + public interface ILattice where TValue : IEquatable + { + // We require that the lattice has a "Top" or maximum element. + // Top is >= a for every element a of the lattice. + // Top is an identity for Meet: Meet(a, Top) = a. + + // The typical use in a dataflow analysis is for Top to represent the "unknown" initial state + // with the least possible information about the analysis. + public TValue Top { get; } + + // The Meet operation is associative, commutative, and idempotent. + // This is used in dataflow analysis to iteratively Meet the tracked facts from different control + // flow paths until the analysis converges to the most specific set of tracked facts. + public TValue Meet (TValue left, TValue right); + } +} diff --git a/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ITransfer.cs b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ITransfer.cs new file mode 100644 index 0000000000000..74c35c8613d5b --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ITransfer.cs @@ -0,0 +1,38 @@ +// 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; + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +namespace ILLink.Shared.DataFlow +{ + // ITransfer represents the transfer functions for a dataflow analysis. + // The transfer functions compute the effects of an operation on the set of facts + // tracked by a dataflow analysis. This simulates the execution of the operation + // on the domain of abstract values tracked in the dataflow analysis. + + // TValue is the type of the information tracked in a dataflow analysis at each program point. + // TLattice is the type of the lattice formed by these values. + + // TOperation is a type representing the operations that the transfer function + // "simulates". It isn't constrained by the interface, but is typically a basic block, + // where the transfer functions are defined in terms of transfer functions for individual + // operations in the block. + + // TLattice isn't typically used in the implementation except to provide the "Top" value. + // This expresses the conceptual constraint that the transferred values are part of a lattice. + public interface ITransfer + where TValue : struct, IEquatable + where TState : class, IDataFlowState + where TLattice : ILattice + { + // Transfer should mutate the input value to reflect the effect of + // computing this operation. When using value types, ensure that + // any modifications to the values are observable by the caller (consider + // using readonly structs to prevent the implementation from making changes + // that won't be reflected in the caller). + void Transfer (TOperation operation, TState state); + } +} diff --git a/src/coreclr/tools/aot/ILLink.Shared/DataFlow/SingleValue.cs b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/SingleValue.cs new file mode 100644 index 0000000000000..79963757b9346 --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/SingleValue.cs @@ -0,0 +1,24 @@ +// 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. + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +namespace ILLink.Shared.DataFlow +{ + // This is a sum type over the various kinds of values we track: + // - dynamicallyaccessedmembertypes-annotated locations (types or strings) + // - known typeof values and similar + // - known strings + // - known integers + + public abstract record SingleValue : IDeepCopyValue + { + // All values must explicitely declare their ability to deep copy itself. + // If the value is immutable, it can "return this" as an optimization. + // Note: Since immutability is relatively tricky to determine, we require all values + // to explicitly implement the DeepCopy, even though the expectation is that + // most values will just "return this". + public abstract SingleValue DeepCopy (); + } +} diff --git a/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ValueSet.cs b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ValueSet.cs new file mode 100644 index 0000000000000..120e9116eaf37 --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ValueSet.cs @@ -0,0 +1,193 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +namespace ILLink.Shared.DataFlow +{ + public readonly struct ValueSet : IEquatable>, IEnumerable + where TValue : notnull + { + // Since we're going to do lot of type checks for this class a lot, it is much more efficient + // if the class is sealed (as then the runtime can do a simple method table pointer comparison) + sealed class EnumerableValues : HashSet + { + public EnumerableValues (IEnumerable values) : base (values) { } + + public override int GetHashCode () + { + int hashCode = 0; + foreach (var item in this) + hashCode = HashUtils.Combine (hashCode, item); + return hashCode; + } + } + + public struct Enumerator : IEnumerator, IDisposable, IEnumerator + { + readonly object? _value; + int _state; // 0 before begining, 1 at item, 2 after end + readonly IEnumerator? _enumerator; + + internal Enumerator (object? values) + { + _state = 0; + if (values is EnumerableValues valuesSet) { + _enumerator = valuesSet.GetEnumerator (); + _value = null; + } else { + _enumerator = null; + _value = values; + } + } + + public TValue Current => _enumerator is not null + ? _enumerator.Current + : (_state == 1 ? (TValue) _value! : default!); + + object? IEnumerator.Current => Current; + + public void Dispose () + { + } + + public bool MoveNext () + { + if (_enumerator is not null) + return _enumerator.MoveNext (); + + if (_value is null) + return false; + + if (_state > 1) + return false; + + _state++; + return _state == 1; + } + + public void Reset () + { + if (_enumerator is not null) + _enumerator.Reset (); + else + _state = 0; + } + } + + // This stores the values. By far the most common case will be either no values, or a single value. + // Cases where there are multiple values stored are relatively very rare. + // null - no values (empty set) + // TValue - single value itself + // EnumerableValues typed object - multiple values, stored in the hashset + readonly object? _values; + + public ValueSet (TValue value) => _values = value; + + public ValueSet (IEnumerable values) => _values = new EnumerableValues (values); + + ValueSet (EnumerableValues values) => _values = values; + + public static implicit operator ValueSet (TValue value) => new (value); + + public override bool Equals (object? obj) => obj is ValueSet other && Equals (other); + + public bool Equals (ValueSet other) + { + if (_values == null) + return other._values == null; + if (other._values == null) + return false; + + if (_values is EnumerableValues enumerableValues) { + if (other._values is EnumerableValues otherValuesSet) { + return enumerableValues.SetEquals (otherValuesSet); + } else + return false; + } else { + if (other._values is EnumerableValues) { + return false; + } + + return EqualityComparer.Default.Equals ((TValue) _values, (TValue) other._values); + } + } + + public override int GetHashCode () + { + if (_values == null) + return typeof (ValueSet).GetHashCode (); + + if (_values is EnumerableValues enumerableValues) + return enumerableValues.GetHashCode (); + + return _values.GetHashCode (); + } + + public Enumerator GetEnumerator () => new (_values); + + IEnumerator IEnumerable.GetEnumerator () => GetEnumerator (); + + IEnumerator IEnumerable.GetEnumerator () => GetEnumerator (); + + public bool Contains (TValue value) => _values is null + ? false + : _values is EnumerableValues valuesSet + ? valuesSet.Contains (value) + : EqualityComparer.Default.Equals (value, (TValue) _values); + + internal static ValueSet Meet (ValueSet left, ValueSet right) + { + if (left._values == null) + return right.Clone (); + if (right._values == null) + return left.Clone (); + + if (left._values is not EnumerableValues && right.Contains ((TValue) left._values)) + return right.Clone (); + + if (right._values is not EnumerableValues && left.Contains ((TValue) right._values)) + return left.Clone (); + + var values = new EnumerableValues (left.Clone ()); + values.UnionWith (right.Clone ()); + return new ValueSet (values); + } + + public bool IsEmpty () => _values == null; + + public override string ToString () + { + StringBuilder sb = new (); + sb.Append ("{"); + sb.Append (string.Join (",", this.Select (v => v.ToString ()))); + sb.Append ("}"); + return sb.ToString (); + } + + // Meet should copy the values, but most SingleValues are immutable. + // Clone returns `this` if there are no mutable SingleValues (SingleValues that implement IDeepCopyValue), otherwise creates a new ValueSet with copies of the copiable Values + public ValueSet Clone () + { + if (_values is null) + return this; + + // Optimize for the most common case with only a single value + if (_values is not EnumerableValues) { + if (_values is IDeepCopyValue copyValue) + return new ValueSet (copyValue.DeepCopy ()); + else + return this; + } + + return new ValueSet (this.Select (value => value is IDeepCopyValue copyValue ? copyValue.DeepCopy () : value)); + } + } +} diff --git a/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ValueSetLattice.cs b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ValueSetLattice.cs new file mode 100644 index 0000000000000..1acca955ce046 --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/DataFlow/ValueSetLattice.cs @@ -0,0 +1,19 @@ +// 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; + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +namespace ILLink.Shared.DataFlow +{ + // A lattice over ValueSets where the Meet operation is just set union. + public readonly struct ValueSetLattice : ILattice> + where TValue : IEquatable + { + public ValueSet Top => default; + + public ValueSet Meet (ValueSet left, ValueSet right) => ValueSet.Meet (left, right); + } +} diff --git a/src/coreclr/tools/aot/ILLink.Shared/DiagnosticCategory.cs b/src/coreclr/tools/aot/ILLink.Shared/DiagnosticCategory.cs index 427c7fabf8f73..a1d1e116f7736 100644 --- a/src/coreclr/tools/aot/ILLink.Shared/DiagnosticCategory.cs +++ b/src/coreclr/tools/aot/ILLink.Shared/DiagnosticCategory.cs @@ -1,12 +1,15 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// 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. + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable namespace ILLink.Shared { - internal static class DiagnosticCategory - { - public const string SingleFile = nameof(SingleFile); - public const string Trimming = nameof(Trimming); - public const string AOT = nameof(AOT); - } + internal static class DiagnosticCategory + { + public const string SingleFile = nameof (SingleFile); + public const string Trimming = nameof (Trimming); + public const string AOT = nameof (AOT); + } } diff --git a/src/coreclr/tools/aot/ILLink.Shared/DiagnosticId.cs b/src/coreclr/tools/aot/ILLink.Shared/DiagnosticId.cs index e22574dbd335c..ac417f4ee58e6 100644 --- a/src/coreclr/tools/aot/ILLink.Shared/DiagnosticId.cs +++ b/src/coreclr/tools/aot/ILLink.Shared/DiagnosticId.cs @@ -1,218 +1,224 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// 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. + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable namespace ILLink.Shared { - public enum DiagnosticId - { - // Linker error ids. - XmlFeatureDoesNotSpecifyFeatureValue = 1001, - XmlUnsupportedNonBooleanValueForFeature = 1002, - XmlException = 1003, - _unused_FailedToProcessDescriptorFile = 1004, - CouldNotFindMethodInAssembly = 1005, - CannotStubConstructorWhenBaseTypeDoesNotHaveConstructor = 1006, - CouldNotFindType = 1007, - CouldNotFindConstructor = 1008, - CouldNotFindAssemblyReference = 1009, - CouldNotLoadAssembly = 1010, - FailedToWriteOutput = 1011, - LinkerUnexpectedError = 1012, - ErrorProcessingXmlLocation = 1013, - XmlDocumentLocationHasInvalidFeatureDefault = 1014, - UnrecognizedCommandLineOption = 1015, - InvalidWarningVersion = 1016, - InvalidGenerateWarningSuppressionsValue = 1017, - MissingArgumentForCommanLineOptionName = 1018, - CustomDataFormatIsInvalid = 1019, - NoFilesToLinkSpecified = 1020, - NewMvidAndDeterministicCannotBeUsedAtSameTime = 1021, - AssemblyInCustomStepOptionCouldNotBeFound = 1022, - AssemblyPathInCustomStepMustBeFullyQualified = 1023, - InvalidArgForCustomStep = 1024, - ExpectedSignToControlNewStepInsertion = 1025, - PipelineStepCouldNotBeFound = 1026, - CustomStepTypeCouldNotBeFound = 1027, - CustomStepTypeIsIncompatibleWithLinkerVersion = 1028, - InvalidOptimizationValue = 1029, - InvalidArgumentForTokenOption = 1030, - InvalidAssemblyAction = 1031, - RootAssemblyCouldNotBeFound = 1032, - XmlDescriptorCouldNotBeFound = 1033, - RootAssemblyDoesNotHaveEntryPoint = 1034, - RootAssemblyCannotUseAction = 1035, - InvalidAssemblyName = 1036, - InvalidAssemblyRootMode = 1037, - ExportedTypeCannotBeResolved = 1038, - ReferenceAssemblyCouldNotBeLoaded = 1039, - FailedToResolveMetadataElement = 1040, - TypeUsedWithAttributeValueCouldNotBeFound = 1041, - CannotConverValueToType = 1042, - CustomAttributeArgumentForTypeRequiresNestedNode = 1043, - CouldNotResolveCustomAttributeTypeValue = 1044, - UnexpectedAttributeArgumentType = 1045, - InvalidMetadataOption = 1046, + public enum DiagnosticId + { + // Linker error ids. + XmlFeatureDoesNotSpecifyFeatureValue = 1001, + XmlUnsupportedNonBooleanValueForFeature = 1002, + XmlException = 1003, + _unused_FailedToProcessDescriptorFile = 1004, + CouldNotFindMethodInAssembly = 1005, + CannotStubConstructorWhenBaseTypeDoesNotHaveConstructor = 1006, + CouldNotFindType = 1007, + CouldNotFindConstructor = 1008, + CouldNotFindAssemblyReference = 1009, + CouldNotLoadAssembly = 1010, + FailedToWriteOutput = 1011, + LinkerUnexpectedError = 1012, + ErrorProcessingXmlLocation = 1013, + XmlDocumentLocationHasInvalidFeatureDefault = 1014, + UnrecognizedCommandLineOption = 1015, + InvalidWarningVersion = 1016, + InvalidGenerateWarningSuppressionsValue = 1017, + MissingArgumentForCommanLineOptionName = 1018, + CustomDataFormatIsInvalid = 1019, + NoFilesToLinkSpecified = 1020, + NewMvidAndDeterministicCannotBeUsedAtSameTime = 1021, + AssemblyInCustomStepOptionCouldNotBeFound = 1022, + AssemblyPathInCustomStepMustBeFullyQualified = 1023, + InvalidArgForCustomStep = 1024, + ExpectedSignToControlNewStepInsertion = 1025, + PipelineStepCouldNotBeFound = 1026, + CustomStepTypeCouldNotBeFound = 1027, + CustomStepTypeIsIncompatibleWithLinkerVersion = 1028, + InvalidOptimizationValue = 1029, + InvalidArgumentForTokenOption = 1030, + InvalidAssemblyAction = 1031, + RootAssemblyCouldNotBeFound = 1032, + XmlDescriptorCouldNotBeFound = 1033, + RootAssemblyDoesNotHaveEntryPoint = 1034, + RootAssemblyCannotUseAction = 1035, + InvalidAssemblyName = 1036, + InvalidAssemblyRootMode = 1037, + ExportedTypeCannotBeResolved = 1038, + ReferenceAssemblyCouldNotBeLoaded = 1039, + FailedToResolveMetadataElement = 1040, + TypeUsedWithAttributeValueCouldNotBeFound = 1041, + CannotConverValueToType = 1042, + CustomAttributeArgumentForTypeRequiresNestedNode = 1043, + CouldNotResolveCustomAttributeTypeValue = 1044, + UnexpectedAttributeArgumentType = 1045, + InvalidMetadataOption = 1046, + + // Linker diagnostic ids. + TypeHasNoFieldsToPreserve = 2001, + TypeHasNoMethodsToPreserve = 2002, + CouldNotResolveDependencyAssembly = 2003, + CouldNotResolveDependencyType = 2004, + CouldNotResolveDependencyMember = 2005, + _unused_UnrecognizedReflectionPattern = 2006, + XmlCouldNotResolveAssembly = 2007, + XmlCouldNotResolveType = 2008, + XmlCouldNotFindMethodOnType = 2009, + XmlInvalidValueForStub = 2010, + XmlUnkownBodyModification = 2011, + XmlCouldNotFindFieldOnType = 2012, + XmlSubstitutedFieldNeedsToBeStatic = 2013, + XmlMissingSubstitutionValueForField = 2014, + XmlInvalidSubstitutionValueForField = 2015, + XmlCouldNotFindEventOnType = 2016, + XmlCouldNotFindPropertyOnType = 2017, + XmlCouldNotFindGetAccesorOfPropertyOnType = 2018, + XmlCouldNotFindSetAccesorOfPropertyOnType = 2019, + _unused_RearrangedXmlWarning1 = 2020, + _unused_RearrangedXmlWarning2 = 2021, + XmlCouldNotFindMatchingConstructorForCustomAttribute = 2022, + XmlMoreThanOneReturnElementForMethod = 2023, + XmlMoreThanOneValyForParameterOfMethod = 2024, + XmlDuplicatePreserveMember = 2025, + RequiresUnreferencedCode = 2026, + AttributeShouldOnlyBeUsedOnceOnMember = 2027, + AttributeDoesntHaveTheRequiredNumberOfParameters = 2028, + XmlElementDoesNotContainRequiredAttributeFullname = 2029, + XmlCouldNotResolveAssemblyForAttribute = 2030, + XmlAttributeTypeCouldNotBeFound = 2031, + UnrecognizedParameterInMethodCreateInstance = 2032, + DeprecatedPreserveDependencyAttribute = 2033, + DynamicDependencyAttributeCouldNotBeAnalyzed = 2034, + UnresolvedAssemblyInDynamicDependencyAttribute = 2035, + UnresolvedTypeInDynamicDependencyAttribute = 2036, + NoMembersResolvedForMemberSignatureOrType = 2037, + XmlMissingNameAttributeInResource = 2038, + XmlInvalidValueForAttributeActionForResource = 2039, + XmlCouldNotFindResourceToRemoveInAssembly = 2040, + DynamicallyAccessedMembersIsNotAllowedOnMethods = 2041, + DynamicallyAccessedMembersCouldNotFindBackingField = 2042, + DynamicallyAccessedMembersConflictsBetweenPropertyAndAccessor = 2043, + XmlCouldNotFindAnyTypeInNamespace = 2044, + AttributeIsReferencedButTrimmerRemoveAllInstances = 2045, + RequiresUnreferencedCodeAttributeMismatch = 2046, + _unused_DynamicallyAccessedMembersMismatchBetweenOverrides = 2047, + XmlRemoveAttributeInstancesCanOnlyBeUsedOnType = 2048, + UnrecognizedInternalAttribute = 2049, + CorrectnessOfCOMCannotBeGuaranteed = 2050, + XmlPropertyDoesNotContainAttributeName = 2051, + XmlCouldNotFindProperty = 2052, + _unused_XmlInvalidPropertyValueForProperty = 2053, + _unused_XmlInvalidArgumentForParameterOfType = 2054, + MakeGenericType = 2055, + DynamicallyAccessedMembersOnPropertyConflictsWithBackingField = 2056, + UnrecognizedTypeNameInTypeGetType = 2057, + ParametersOfAssemblyCreateInstanceCannotBeAnalyzed = 2058, + UnrecognizedTypeInRuntimeHelpersRunClassConstructor = 2059, + MakeGenericMethod = 2060, + UnresolvedAssemblyInCreateInstance = 2061, - // Linker diagnostic ids. - TypeHasNoFieldsToPreserve = 2001, - TypeHasNoMethodsToPreserve = 2002, - CouldNotResolveDependencyAssembly = 2003, - CouldNotResolveDependencyType = 2004, - CouldNotResolveDependencyMember = 2005, - _unused_UnrecognizedReflectionPattern = 2006, - XmlCouldNotResolveAssembly = 2007, - XmlCouldNotResolveType = 2008, - XmlCouldNotFindMethodOnType = 2009, - XmlInvalidValueForStub = 2010, - XmlUnkownBodyModification = 2011, - XmlCouldNotFindFieldOnType = 2012, - XmlSubstitutedFieldNeedsToBeStatic = 2013, - XmlMissingSubstitutionValueForField = 2014, - XmlInvalidSubstitutionValueForField = 2015, - XmlCouldNotFindEventOnType = 2016, - XmlCouldNotFindPropertyOnType = 2017, - XmlCouldNotFindGetAccesorOfPropertyOnType = 2018, - XmlCouldNotFindSetAccesorOfPropertyOnType = 2019, - _unused_RearrangedXmlWarning1 = 2020, - _unused_RearrangedXmlWarning2 = 2021, - XmlCouldNotFindMatchingConstructorForCustomAttribute = 2022, - XmlMoreThanOneReturnElementForMethod = 2023, - XmlMoreThanOneValueForParameterOfMethod = 2024, - XmlDuplicatePreserveMember = 2025, - RequiresUnreferencedCode = 2026, - AttributeShouldOnlyBeUsedOnceOnMember = 2027, - AttributeDoesntHaveTheRequiredNumberOfParameters = 2028, - XmlElementDoesNotContainRequiredAttributeFullname = 2029, - XmlCouldNotResolveAssemblyForAttribute = 2030, - XmlAttributeTypeCouldNotBeFound = 2031, - UnrecognizedParameterInMethodCreateInstance = 2032, - DeprecatedPreserveDependencyAttribute = 2033, - DynamicDependencyAttributeCouldNotBeAnalyzed = 2034, - UnresolvedAssemblyInDynamicDependencyAttribute = 2035, - UnresolvedTypeInDynamicDependencyAttribute = 2036, - NoMembersResolvedForMemberSignatureOrType = 2037, - XmlMissingNameAttributeInResource = 2038, - XmlInvalidValueForAttributeActionForResource = 2039, - XmlCouldNotFindResourceToRemoveInAssembly = 2040, - DynamicallyAccessedMembersIsNotAllowedOnMethods = 2041, - DynamicallyAccessedMembersCouldNotFindBackingField = 2042, - DynamicallyAccessedMembersConflictsBetweenPropertyAndAccessor = 2043, - XmlCouldNotFindAnyTypeInNamespace = 2044, - AttributeIsReferencedButTrimmerRemoveAllInstances = 2045, - RequiresUnreferencedCodeAttributeMismatch = 2046, - _unused_DynamicallyAccessedMembersMismatchBetweenOverrides = 2047, - XmlRemoveAttributeInstancesCanOnlyBeUsedOnType = 2048, - _unused_UnrecognizedInternalAttribute = 2049, - CorrectnessOfCOMCannotBeGuaranteed = 2050, - XmlPropertyDoesNotContainAttributeName = 2051, - XmlCouldNotFindProperty = 2052, - _unused_XmlInvalidPropertyValueForProperty = 2053, - _unused_XmlInvalidArgumentForParameterOfType = 2054, - MakeGenericType = 2055, - DynamicallyAccessedMembersOnPropertyConflictsWithBackingField = 2056, - UnrecognizedTypeNameInTypeGetType = 2057, - ParametersOfAssemblyCreateInstanceCannotBeAnalyzed = 2058, - UnrecognizedTypeInRuntimeHelpersRunClassConstructor = 2059, - MakeGenericMethod = 2060, - UnresolvedAssemblyInCreateInstance = 2061, - MethodParameterCannotBeStaticallyDetermined = 2062, - MethodReturnValueCannotBeStaticallyDetermined = 2063, - FieldValueCannotBeStaticallyDetermined = 2064, - ImplicitThisCannotBeStaticallyDetermined = 2065, - TypePassedToGenericParameterCannotBeStaticallyDetermined = 2066, + // Unknown value used in a place which requires annotation + MethodParameterCannotBeStaticallyDetermined = 2062, + MethodReturnValueCannotBeStaticallyDetermined = 2063, + FieldValueCannotBeStaticallyDetermined = 2064, + ImplicitThisCannotBeStaticallyDetermined = 2065, + TypePassedToGenericParameterCannotBeStaticallyDetermined = 2066, - // Dynamically Accessed Members attribute mismatch. - DynamicallyAccessedMembersMismatchParameterTargetsParameter = 2067, - DynamicallyAccessedMembersMismatchParameterTargetsMethodReturnType = 2068, - DynamicallyAccessedMembersMismatchParameterTargetsField = 2069, - DynamicallyAccessedMembersMismatchParameterTargetsThisParameter = 2070, - DynamicallyAccessedMembersMismatchParameterTargetsGenericParameter = 2071, - DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsParameter = 2072, - DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsMethodReturnType = 2073, - DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsField = 2074, - DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsThisParameter = 2075, - DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsGenericParameter = 2076, - DynamicallyAccessedMembersMismatchFieldTargetsParameter = 2077, - DynamicallyAccessedMembersMismatchFieldTargetsMethodReturnType = 2078, - DynamicallyAccessedMembersMismatchFieldTargetsField = 2079, - DynamicallyAccessedMembersMismatchFieldTargetsThisParameter = 2080, - DynamicallyAccessedMembersMismatchFieldTargetsGenericParameter = 2081, - DynamicallyAccessedMembersMismatchThisParameterTargetsParameter = 2082, - DynamicallyAccessedMembersMismatchThisParameterTargetsMethodReturnType = 2083, - DynamicallyAccessedMembersMismatchThisParameterTargetsField = 2084, - DynamicallyAccessedMembersMismatchThisParameterTargetsThisParameter = 2085, - DynamicallyAccessedMembersMismatchThisParameterTargetsGenericParameter = 2086, - DynamicallyAccessedMembersMismatchTypeArgumentTargetsParameter = 2087, - DynamicallyAccessedMembersMismatchTypeArgumentTargetsMethodReturnType = 2088, - DynamicallyAccessedMembersMismatchTypeArgumentTargetsField = 2089, - DynamicallyAccessedMembersMismatchTypeArgumentTargetsThisParameter = 2090, - DynamicallyAccessedMembersMismatchTypeArgumentTargetsGenericParameter = 2091, - DynamicallyAccessedMembersMismatchOnMethodParameterBetweenOverrides = 2092, - DynamicallyAccessedMembersMismatchOnMethodReturnValueBetweenOverrides = 2093, - DynamicallyAccessedMembersMismatchOnImplicitThisBetweenOverrides = 2094, - DynamicallyAccessedMembersMismatchOnGenericParameterBetweenOverrides = 2095, + // Dynamically Accessed Members attribute mismatch. + DynamicallyAccessedMembersMismatchParameterTargetsParameter = 2067, + DynamicallyAccessedMembersMismatchParameterTargetsMethodReturnType = 2068, + DynamicallyAccessedMembersMismatchParameterTargetsField = 2069, + DynamicallyAccessedMembersMismatchParameterTargetsThisParameter = 2070, + DynamicallyAccessedMembersMismatchParameterTargetsGenericParameter = 2071, + DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsParameter = 2072, + DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsMethodReturnType = 2073, + DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsField = 2074, + DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsThisParameter = 2075, + DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsGenericParameter = 2076, + DynamicallyAccessedMembersMismatchFieldTargetsParameter = 2077, + DynamicallyAccessedMembersMismatchFieldTargetsMethodReturnType = 2078, + DynamicallyAccessedMembersMismatchFieldTargetsField = 2079, + DynamicallyAccessedMembersMismatchFieldTargetsThisParameter = 2080, + DynamicallyAccessedMembersMismatchFieldTargetsGenericParameter = 2081, + DynamicallyAccessedMembersMismatchThisParameterTargetsParameter = 2082, + DynamicallyAccessedMembersMismatchThisParameterTargetsMethodReturnType = 2083, + DynamicallyAccessedMembersMismatchThisParameterTargetsField = 2084, + DynamicallyAccessedMembersMismatchThisParameterTargetsThisParameter = 2085, + DynamicallyAccessedMembersMismatchThisParameterTargetsGenericParameter = 2086, + DynamicallyAccessedMembersMismatchTypeArgumentTargetsParameter = 2087, + DynamicallyAccessedMembersMismatchTypeArgumentTargetsMethodReturnType = 2088, + DynamicallyAccessedMembersMismatchTypeArgumentTargetsField = 2089, + DynamicallyAccessedMembersMismatchTypeArgumentTargetsThisParameter = 2090, + DynamicallyAccessedMembersMismatchTypeArgumentTargetsGenericParameter = 2091, + DynamicallyAccessedMembersMismatchOnMethodParameterBetweenOverrides = 2092, + DynamicallyAccessedMembersMismatchOnMethodReturnValueBetweenOverrides = 2093, + DynamicallyAccessedMembersMismatchOnImplicitThisBetweenOverrides = 2094, + DynamicallyAccessedMembersMismatchOnGenericParameterBetweenOverrides = 2095, - CaseInsensitiveTypeGetTypeCallIsNotSupported = 2096, - DynamicallyAccessedMembersOnFieldCanOnlyApplyToTypesOrStrings = 2097, - DynamicallyAccessedMembersOnMethodParameterCanOnlyApplyToTypesOrStrings = 2098, - DynamicallyAccessedMembersOnPropertyCanOnlyApplyToTypesOrStrings = 2099, - XmlUnsuportedWildcard = 2100, - AssemblyWithEmbeddedXmlApplyToAnotherAssembly = 2101, - InvalidIsTrimmableValue = 2102, - PropertyAccessorParameterInLinqExpressionsCannotBeStaticallyDetermined = 2103, - AssemblyProducedTrimWarnings = 2104, - TypeWasNotFoundInAssemblyNorBaseLibrary = 2105, - DynamicallyAccessedMembersOnMethodReturnValueCanOnlyApplyToTypesOrStrings = 2106, - MethodsAreAssociatedWithStateMachine = 2107, - InvalidScopeInUnconditionalSuppressMessage = 2108, - RequiresUnreferencedCodeOnBaseClass = 2109, - DynamicallyAccessedMembersFieldAccessedViaReflection = 2110, - DynamicallyAccessedMembersMethodAccessedViaReflection = 2111, - DynamicallyAccessedMembersOnTypeReferencesMemberWithRequiresUnreferencedCode = 2112, - DynamicallyAccessedMembersOnTypeReferencesMemberOnBaseWithRequiresUnreferencedCode = 2113, - DynamicallyAccessedMembersOnTypeReferencesMemberWithDynamicallyAccessedMembers = 2114, - DynamicallyAccessedMembersOnTypeReferencesMemberOnBaseWithDynamicallyAccessedMembers = 2115, - RequiresUnreferencedCodeOnStaticConstructor = 2116, + CaseInsensitiveTypeGetTypeCallIsNotSupported = 2096, + DynamicallyAccessedMembersOnFieldCanOnlyApplyToTypesOrStrings = 2097, + DynamicallyAccessedMembersOnMethodParameterCanOnlyApplyToTypesOrStrings = 2098, + DynamicallyAccessedMembersOnPropertyCanOnlyApplyToTypesOrStrings = 2099, + XmlUnsuportedWildcard = 2100, + AssemblyWithEmbeddedXmlApplyToAnotherAssembly = 2101, + InvalidIsTrimmableValue = 2102, + PropertyAccessorParameterInLinqExpressionsCannotBeStaticallyDetermined = 2103, + AssemblyProducedTrimWarnings = 2104, + TypeWasNotFoundInAssemblyNorBaseLibrary = 2105, + DynamicallyAccessedMembersOnMethodReturnValueCanOnlyApplyToTypesOrStrings = 2106, + MethodsAreAssociatedWithStateMachine = 2107, + InvalidScopeInUnconditionalSuppressMessage = 2108, + RequiresUnreferencedCodeOnBaseClass = 2109, + DynamicallyAccessedMembersFieldAccessedViaReflection = 2110, + DynamicallyAccessedMembersMethodAccessedViaReflection = 2111, + DynamicallyAccessedMembersOnTypeReferencesMemberWithRequiresUnreferencedCode = 2112, + DynamicallyAccessedMembersOnTypeReferencesMemberOnBaseWithRequiresUnreferencedCode = 2113, + DynamicallyAccessedMembersOnTypeReferencesMemberWithDynamicallyAccessedMembers = 2114, + DynamicallyAccessedMembersOnTypeReferencesMemberOnBaseWithDynamicallyAccessedMembers = 2115, + RequiresUnreferencedCodeOnStaticConstructor = 2116, + MethodsAreAssociatedWithUserMethod = 2117, - // Single-file diagnostic ids. - AvoidAssemblyLocationInSingleFile = 3000, - AvoidAssemblyGetFilesInSingleFile = 3001, - RequiresAssemblyFiles = 3002, - RequiresAssemblyFilesAttributeMismatch = 3003, + // Single-file diagnostic ids. + AvoidAssemblyLocationInSingleFile = 3000, + AvoidAssemblyGetFilesInSingleFile = 3001, + RequiresAssemblyFiles = 3002, + RequiresAssemblyFilesAttributeMismatch = 3003, + RequiresAssemblyFilesOnStaticConstructor = 3004, - // Dynamic code diagnostic ids. - RequiresDynamicCode = 3050, - RequiresDynamicCodeAttributeMismatch = 3051, - COMInteropNotSupportedInFullAOT = 3052, - AssemblyProducedAOTWarnings = 3053, - GenericRecursionCycle = 3054, - CorrectnessOfAbstractDelegatesCannotBeGuaranteed = 3055, - RequiresDynamicCodeOnStaticConstructor = 3056, - } + // Dynamic code diagnostic ids. + RequiresDynamicCode = 3050, + RequiresDynamicCodeAttributeMismatch = 3051, + COMInteropNotSupportedInFullAOT = 3052, + AssemblyProducedAOTWarnings = 3053, + GenericRecursionCycle = 3054, + CorrectnessOfAbstractDelegatesCannotBeGuaranteed = 3055, + RequiresDynamicCodeOnStaticConstructor = 3056, + } - public static class DiagnosticIdExtensions - { - public static string AsString(this DiagnosticId diagnosticId) => $"IL{(int)diagnosticId}"; + public static class DiagnosticIdExtensions + { + public static string AsString (this DiagnosticId diagnosticId) => $"IL{(int) diagnosticId}"; - public static string GetDiagnosticSubcategory(this DiagnosticId diagnosticId) => - (int)diagnosticId switch - { - 2026 => MessageSubCategory.TrimAnalysis, - 2032 => MessageSubCategory.TrimAnalysis, - 2041 => MessageSubCategory.TrimAnalysis, - 2042 => MessageSubCategory.TrimAnalysis, - 2043 => MessageSubCategory.TrimAnalysis, - 2045 => MessageSubCategory.TrimAnalysis, - 2046 => MessageSubCategory.TrimAnalysis, - 2050 => MessageSubCategory.TrimAnalysis, - >= 2055 and <= 2099 => MessageSubCategory.TrimAnalysis, - 2103 => MessageSubCategory.TrimAnalysis, - 2106 => MessageSubCategory.TrimAnalysis, - 2107 => MessageSubCategory.TrimAnalysis, - >= 2109 and <= 2116 => MessageSubCategory.TrimAnalysis, - >= 3050 and <= 3052 => MessageSubCategory.AotAnalysis, - >= 3054 and <= 3055 => MessageSubCategory.AotAnalysis, - _ => MessageSubCategory.None, - }; - } + public static string GetDiagnosticSubcategory (this DiagnosticId diagnosticId) => + (int) diagnosticId switch { + 2026 => MessageSubCategory.TrimAnalysis, + 2032 => MessageSubCategory.TrimAnalysis, + 2041 => MessageSubCategory.TrimAnalysis, + 2042 => MessageSubCategory.TrimAnalysis, + 2043 => MessageSubCategory.TrimAnalysis, + 2045 => MessageSubCategory.TrimAnalysis, + 2046 => MessageSubCategory.TrimAnalysis, + 2050 => MessageSubCategory.TrimAnalysis, + >= 2055 and <= 2099 => MessageSubCategory.TrimAnalysis, + 2103 => MessageSubCategory.TrimAnalysis, + 2106 => MessageSubCategory.TrimAnalysis, + 2107 => MessageSubCategory.TrimAnalysis, + >= 2109 and <= 2116 => MessageSubCategory.TrimAnalysis, + >= 3050 and <= 3052 => MessageSubCategory.AotAnalysis, + >= 3054 and <= 3055 => MessageSubCategory.AotAnalysis, + _ => MessageSubCategory.None, + }; + } } diff --git a/src/coreclr/tools/aot/ILLink.Shared/DiagnosticString.cs b/src/coreclr/tools/aot/ILLink.Shared/DiagnosticString.cs index f14b58b060083..0102ae90c22c2 100644 --- a/src/coreclr/tools/aot/ILLink.Shared/DiagnosticString.cs +++ b/src/coreclr/tools/aot/ILLink.Shared/DiagnosticString.cs @@ -1,37 +1,40 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// 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; +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + namespace ILLink.Shared { - public readonly struct DiagnosticString - { - readonly string _titleFormat; - readonly string _messageFormat; - - public DiagnosticString(DiagnosticId diagnosticId) - { - var resourceManager = SharedStrings.ResourceManager; - _titleFormat = resourceManager.GetString($"{diagnosticId}Title") ?? throw new InvalidOperationException($"{diagnosticId} does not have a matching resource called {diagnosticId}Title"); - _messageFormat = resourceManager.GetString($"{diagnosticId}Message") ?? throw new InvalidOperationException($"{diagnosticId} does not have a matching resource called {diagnosticId}Message"); - } - - public DiagnosticString(string diagnosticResourceStringName) - { - var resourceManager = SharedStrings.ResourceManager; - _titleFormat = resourceManager.GetString($"{diagnosticResourceStringName}Title") ?? throw new InvalidOperationException($"{diagnosticResourceStringName} does not have a matching resource called {diagnosticResourceStringName}Title"); - _messageFormat = resourceManager.GetString($"{diagnosticResourceStringName}Message") ?? throw new InvalidOperationException($"{diagnosticResourceStringName} does not have a matching resource called {diagnosticResourceStringName}Message"); - } - - public string GetMessage(params string[] args) => - string.Format(_messageFormat, args); - - public string GetMessageFormat() => _messageFormat; - - public string GetTitle(params string[] args) => - string.Format(_titleFormat, args); - - public string GetTitleFormat() => _titleFormat; - } + public readonly struct DiagnosticString + { + readonly string _titleFormat; + readonly string _messageFormat; + + public DiagnosticString (DiagnosticId diagnosticId) + { + var resourceManager = SharedStrings.ResourceManager; + _titleFormat = resourceManager.GetString ($"{diagnosticId}Title") ?? throw new InvalidOperationException ($"{diagnosticId} does not have a matching resource called {diagnosticId}Title"); + _messageFormat = resourceManager.GetString ($"{diagnosticId}Message") ?? throw new InvalidOperationException ($"{diagnosticId} does not have a matching resource called {diagnosticId}Message"); + } + + public DiagnosticString (string diagnosticResourceStringName) + { + var resourceManager = SharedStrings.ResourceManager; + _titleFormat = resourceManager.GetString ($"{diagnosticResourceStringName}Title") ?? throw new InvalidOperationException ($"{diagnosticResourceStringName} does not have a matching resource called {diagnosticResourceStringName}Title"); + _messageFormat = resourceManager.GetString ($"{diagnosticResourceStringName}Message") ?? throw new InvalidOperationException ($"{diagnosticResourceStringName} does not have a matching resource called {diagnosticResourceStringName}Message"); + } + + public string GetMessage (params string[] args) => + string.Format (_messageFormat, args); + + public string GetMessageFormat () => _messageFormat; + + public string GetTitle (params string[] args) => + string.Format (_titleFormat, args); + + public string GetTitleFormat () => _titleFormat; + } } diff --git a/src/coreclr/tools/aot/ILLink.Shared/HashUtils.cs b/src/coreclr/tools/aot/ILLink.Shared/HashUtils.cs new file mode 100644 index 0000000000000..cc9b259bedb9c --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/HashUtils.cs @@ -0,0 +1,42 @@ +// 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. + +#if !NETSTANDARD2_0 +using System; +#endif + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +namespace ILLink.Shared +{ + static class HashUtils + { +#if NETSTANDARD2_0 + // This constant is taken from code that Roslyn generates for GetHashCode of records. + const int Multiplier = -1521134295; +#endif + public static int Combine (T1 value1, T2 value2) + where T1 : notnull + where T2 : notnull + { +#if NETSTANDARD2_0 + return value1.GetHashCode () * Multiplier + value2.GetHashCode (); +#else + return HashCode.Combine (value1, value2); +#endif + } + + public static int Combine (T1 value1, T2 value2, T3 value3) + where T1 : notnull + where T2 : notnull + where T3 : notnull + { +#if NETSTANDARD2_0 + return (value1.GetHashCode () * Multiplier + value2.GetHashCode ()) * Multiplier + value3.GetHashCode (); +#else + return HashCode.Combine (value1, value2, value3); +#endif + } + } +} diff --git a/src/coreclr/tools/aot/ILLink.Shared/ILLink.LinkAttributes.xsd b/src/coreclr/tools/aot/ILLink.Shared/ILLink.LinkAttributes.xsd index f1156b0a4d690..e6c21857a2c73 100644 --- a/src/coreclr/tools/aot/ILLink.Shared/ILLink.LinkAttributes.xsd +++ b/src/coreclr/tools/aot/ILLink.Shared/ILLink.LinkAttributes.xsd @@ -3,122 +3,114 @@ elementFormDefault="unqualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - + \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILLink.Shared/ILLink.Shared.projitems b/src/coreclr/tools/aot/ILLink.Shared/ILLink.Shared.projitems index ffbfab3b98417..66dd85465ebfd 100644 --- a/src/coreclr/tools/aot/ILLink.Shared/ILLink.Shared.projitems +++ b/src/coreclr/tools/aot/ILLink.Shared/ILLink.Shared.projitems @@ -3,17 +3,13 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) true - ff598e93-8e9e-4091-9f50-61a7572663ae + 92f5e753-2179-46dc-bdce-736858c18dc7 ILLink.Shared - - - - - + @@ -23,8 +19,8 @@ - - Designer - - - + + Designer + + + \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILLink.Shared/ILLink.Shared.shproj b/src/coreclr/tools/aot/ILLink.Shared/ILLink.Shared.shproj index d632a98be0ab0..a581f2f0b42ca 100644 --- a/src/coreclr/tools/aot/ILLink.Shared/ILLink.Shared.shproj +++ b/src/coreclr/tools/aot/ILLink.Shared/ILLink.Shared.shproj @@ -1,7 +1,7 @@ - ff598e93-8e9e-4091-9f50-61a7572663ae + {FF598E93-8E9E-4091-9F50-61A7572663AE} 14.0 @@ -10,4 +10,4 @@ - + \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILLink.Shared/IsExternalInit.cs b/src/coreclr/tools/aot/ILLink.Shared/IsExternalInit.cs new file mode 100644 index 0000000000000..f2b081a032f43 --- /dev/null +++ b/src/coreclr/tools/aot/ILLink.Shared/IsExternalInit.cs @@ -0,0 +1,15 @@ +// 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. + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable + +#if NETSTANDARD +// Allow use of init setters on downlevel frameworks. +namespace System.Runtime.CompilerServices +{ + public sealed class IsExternalInit + { + } +} +#endif \ No newline at end of file diff --git a/src/coreclr/tools/aot/ILLink.Shared/MessageFormat.cs b/src/coreclr/tools/aot/ILLink.Shared/MessageFormat.cs index 755279ea65ae1..0e67c71b676ef 100644 --- a/src/coreclr/tools/aot/ILLink.Shared/MessageFormat.cs +++ b/src/coreclr/tools/aot/ILLink.Shared/MessageFormat.cs @@ -1,39 +1,39 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// 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. +// This is needed due to NativeAOT which doesn't enable nullable globally yet #nullable enable namespace ILLink.Shared { - internal static class MessageFormat - { - public static string FormatRequiresAttributeMessageArg(string? message) - { - if (!string.IsNullOrEmpty(message)) - return $" {message}{(message!.TrimEnd().EndsWith(".") ? "" : ".")}"; + internal static class MessageFormat + { + public static string FormatRequiresAttributeMessageArg (string? message) + { + if (!string.IsNullOrEmpty (message)) + return $" {message}{(message!.TrimEnd ().EndsWith (".") ? "" : ".")}"; - return string.Empty; - } + return string.Empty; + } - public static string FormatRequiresAttributeUrlArg(string? url) - { - if (!string.IsNullOrEmpty(url)) - return $" {url}"; + public static string FormatRequiresAttributeUrlArg (string? url) + { + if (!string.IsNullOrEmpty (url)) + return $" {url}"; - return string.Empty; - } + return string.Empty; + } - public static string FormatRequiresAttributeMismatch(bool memberHasAttribute, bool isInterface, params object[] args) - { - string format = (memberHasAttribute, isInterface) switch - { - (false, true) => SharedStrings.InterfaceRequiresMismatchMessage, - (true, true) => SharedStrings.ImplementationRequiresMismatchMessage, - (false, false) => SharedStrings.BaseRequiresMismatchMessage, - (true, false) => SharedStrings.DerivedRequiresMismatchMessage - }; + public static string FormatRequiresAttributeMismatch (bool memberHasAttribute, bool isInterface, params object[] args) + { + string format = (memberHasAttribute, isInterface) switch { + (false, true) => SharedStrings.InterfaceRequiresMismatchMessage, + (true, true) => SharedStrings.ImplementationRequiresMismatchMessage, + (false, false) => SharedStrings.BaseRequiresMismatchMessage, + (true, false) => SharedStrings.DerivedRequiresMismatchMessage + }; - return string.Format(format, args); - } - } + return string.Format (format, args); + } + } } diff --git a/src/coreclr/tools/aot/ILLink.Shared/MessageSubCategory.cs b/src/coreclr/tools/aot/ILLink.Shared/MessageSubCategory.cs index 92c1077b14baf..4b47d26668115 100644 --- a/src/coreclr/tools/aot/ILLink.Shared/MessageSubCategory.cs +++ b/src/coreclr/tools/aot/ILLink.Shared/MessageSubCategory.cs @@ -1,13 +1,16 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// 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. + +// This is needed due to NativeAOT which doesn't enable nullable globally yet +#nullable enable namespace ILLink.Shared { - public static class MessageSubCategory - { - public const string None = ""; - public const string TrimAnalysis = "Trim analysis"; - public const string UnresolvedAssembly = "Unresolved assembly"; - public const string AotAnalysis = "AOT analysis"; - } + public static class MessageSubCategory + { + public const string None = ""; + public const string TrimAnalysis = "Trim analysis"; + public const string UnresolvedAssembly = "Unresolved assembly"; + public const string AotAnalysis = "AOT analysis"; + } } diff --git a/src/coreclr/tools/aot/ILLink.Shared/README.md b/src/coreclr/tools/aot/ILLink.Shared/README.md index 229b67e492bf0..ddd835b1d23cc 100644 --- a/src/coreclr/tools/aot/ILLink.Shared/README.md +++ b/src/coreclr/tools/aot/ILLink.Shared/README.md @@ -1 +1 @@ -Sources taken from https://github.com/dotnet/linker/tree/890591b13da936d2c38a52afdaeac0db69858d4f/src/ILLink.Shared. +Sources taken from https://github.com/dotnet/linker/tree/6aaa900a0b1268fe4a6ce3e8af4678e84ea154fd/src/ILLink.Shared. diff --git a/src/coreclr/tools/aot/ILLink.Shared/SharedStrings.resx b/src/coreclr/tools/aot/ILLink.Shared/SharedStrings.resx index cdcb30468f388..61e172189bd5c 100644 --- a/src/coreclr/tools/aot/ILLink.Shared/SharedStrings.resx +++ b/src/coreclr/tools/aot/ILLink.Shared/SharedStrings.resx @@ -1,4 +1,4 @@ - +