diff --git a/src/coreclr/tools/Common/Compiler/EventPseudoDesc.cs b/src/coreclr/tools/Common/Compiler/EventPseudoDesc.cs index 1bfee2d0196f45..287d86cdee7687 100644 --- a/src/coreclr/tools/Common/Compiler/EventPseudoDesc.cs +++ b/src/coreclr/tools/Common/Compiler/EventPseudoDesc.cs @@ -91,6 +91,8 @@ public EventPseudoDesc(EcmaType type, EventDefinitionHandle handle) public override int GetHashCode() => _type.GetHashCode() ^ _handle.GetHashCode(); + public override string ToString() => $"{_type}.{Name}"; + public static bool operator ==(EventPseudoDesc a, EventPseudoDesc b) => a._type == b._type && a._handle == b._handle; public static bool operator !=(EventPseudoDesc a, EventPseudoDesc b) => !(a == b); diff --git a/src/coreclr/tools/Common/Compiler/PropertyPseudoDesc.cs b/src/coreclr/tools/Common/Compiler/PropertyPseudoDesc.cs index 2c1b021e93e02c..2ceb2d9eda75de 100644 --- a/src/coreclr/tools/Common/Compiler/PropertyPseudoDesc.cs +++ b/src/coreclr/tools/Common/Compiler/PropertyPseudoDesc.cs @@ -86,6 +86,8 @@ public PropertyPseudoDesc(EcmaType type, PropertyDefinitionHandle handle) public override int GetHashCode() => _type.GetHashCode() ^ _handle.GetHashCode(); + public override string ToString() => $"{_type}.{Name}"; + public static bool operator ==(PropertyPseudoDesc a, PropertyPseudoDesc b) => a._type == b._type && a._handle == b._handle; public static bool operator !=(PropertyPseudoDesc a, PropertyPseudoDesc b) => !(a == b); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs index 1b0b36055287f9..7fd15f692c993a 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs @@ -13,7 +13,6 @@ using DependencyListEntry = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.DependencyListEntry; using CombinedDependencyList = System.Collections.Generic.List.CombinedDependencyListEntry>; using CombinedDependencyListEntry = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore.CombinedDependencyListEntry; -using MethodAttributes = System.Reflection.MethodAttributes; namespace ILCompiler.DependencyAnalysis { @@ -54,37 +53,7 @@ public static void AddDependenciesDueToCustomAttributes(ref CombinedDependencyLi AddDependenciesDueToCustomAttributes(ref dependencies, genericParameterCondition, factory, method.Module, parameter.GetCustomAttributes(), method); } - // We don't model properties and events as separate entities within the compiler, so ensuring - // we can generate custom attributes for the associated events and properties from here - // is as good as any other place. - // - // As a performance optimization, we look for associated events and properties only - // if the method is SpecialName. This is required for CLS compliance and compilers we - // care about emit accessors like this. - object propertyCondition = GetMetadataApiDependency(factory, "Property"u8); - if ((methodDef.Attributes & MethodAttributes.SpecialName) != 0) - { - TypeDefinition declaringType = reader.GetTypeDefinition(methodDef.GetDeclaringType()); - - foreach (PropertyDefinitionHandle propertyHandle in declaringType.GetProperties()) - { - PropertyDefinition property = reader.GetPropertyDefinition(propertyHandle); - PropertyAccessors accessors = property.GetAccessors(); - - if (accessors.Getter == methodHandle || accessors.Setter == methodHandle) - AddDependenciesDueToCustomAttributes(ref dependencies, propertyCondition, factory, method.Module, property.GetCustomAttributes(), new PropertyPseudoDesc(method.OwningType, propertyHandle)); - } - - object eventCondition = GetMetadataApiDependency(factory, "Event"u8); - foreach (EventDefinitionHandle eventHandle in declaringType.GetEvents()) - { - EventDefinition @event = reader.GetEventDefinition(eventHandle); - EventAccessors accessors = @event.GetAccessors(); - if (accessors.Adder == methodHandle || accessors.Remover == methodHandle || accessors.Raiser == methodHandle) - AddDependenciesDueToCustomAttributes(ref dependencies, eventCondition, factory, method.Module, @event.GetCustomAttributes(), new EventPseudoDesc(method.OwningType, eventHandle)); - } - } } public static void AddDependenciesDueToCustomAttributes(ref CombinedDependencyList dependencies, NodeFactory factory, EcmaType type) @@ -108,6 +77,16 @@ public static void AddDependenciesDueToCustomAttributes(ref CombinedDependencyLi AddDependenciesDueToCustomAttributes(ref dependencies, GetMetadataApiDependency(factory, "Field"u8), factory, field.Module, fieldDef.GetCustomAttributes(), field); } + public static void AddDependenciesDueToCustomAttributes(ref CombinedDependencyList dependencies, NodeFactory factory, PropertyPseudoDesc property) + { + AddDependenciesDueToCustomAttributes(ref dependencies, GetMetadataApiDependency(factory, "Property"u8), factory, property.OwningType.Module, property.GetCustomAttributes, property); + } + + public static void AddDependenciesDueToCustomAttributes(ref CombinedDependencyList dependencies, NodeFactory factory, EventPseudoDesc @event) + { + AddDependenciesDueToCustomAttributes(ref dependencies, GetMetadataApiDependency(factory, "Event"u8), factory, @event.OwningType.Module, @event.GetCustomAttributes, @event); + } + public static void AddDependenciesDueToCustomAttributes(ref CombinedDependencyList dependencies, NodeFactory factory, EcmaAssembly assembly) { AssemblyDefinition asmDef = assembly.MetadataReader.GetAssemblyDefinition(); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EventMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EventMetadataNode.cs new file mode 100644 index 00000000000000..349b43d253ded7 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EventMetadataNode.cs @@ -0,0 +1,50 @@ +// 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 ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents an event that has metadata generated in the current compilation. + /// This corresponds to an ECMA-335 Event record. Unlike fields and methods, + /// events are not first-class entities in the compiler's type system (there is no + /// interned EventDesc). + /// + /// + /// Only expected to be used during ILScanning when scanning for reflection. + /// + internal sealed class EventMetadataNode : DependencyNodeCore + { + private readonly EventPseudoDesc _event; + + public EventMetadataNode(EventPseudoDesc @event) + { + _event = @event; + } + + public EventPseudoDesc Event => _event; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) => null; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + { + var dependencies = new List(); + CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, _event); + return dependencies; + } + + protected override string GetName(NodeFactory factory) + { + return "Event metadata: " + _event.ToString(); + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => true; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs index f145aa9504eaaa..ab49aca5b7c407 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodMetadataNode.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Metadata; using ILCompiler.Dataflow; using ILCompiler.DependencyAnalysisFramework; @@ -11,6 +13,7 @@ using Debug = System.Diagnostics.Debug; using EcmaMethod = Internal.TypeSystem.Ecma.EcmaMethod; +using EcmaType = Internal.TypeSystem.Ecma.EcmaType; namespace ILCompiler.DependencyAnalysis { @@ -85,6 +88,38 @@ public override IEnumerable GetStaticDependencies(NodeFacto { dependencies.Add(factory.AnalysisCharacteristic("StackTraceHiddenMetadataPresent"), "Method is StackTraceHidden"); } + + // If this method is a property or event accessor, ensure metadata for the associated + // property or event is generated as well. Properties/events are not modeled as first-class + // entities in the type system, so we discover them here from their accessors. + // As a performance optimization, only SpecialName methods can be accessors. + if ((_method.Attributes & MethodAttributes.SpecialName) != 0) + { + MetadataReader reader = _method.MetadataReader; + MethodDefinitionHandle methodHandle = _method.Handle; + TypeDefinition declaringType = reader.GetTypeDefinition(reader.GetMethodDefinition(methodHandle).GetDeclaringType()); + foreach (PropertyDefinitionHandle propertyHandle in declaringType.GetProperties()) + { + PropertyAccessors accessors = reader.GetPropertyDefinition(propertyHandle).GetAccessors(); + if (accessors.Getter == methodHandle || accessors.Setter == methodHandle) + { + dependencies.Add( + factory.PropertyMetadata(new PropertyPseudoDesc((EcmaType)owningType, propertyHandle)), + "Property associated with reflectable accessor"); + } + } + + foreach (EventDefinitionHandle eventHandle in declaringType.GetEvents()) + { + EventAccessors accessors = reader.GetEventDefinition(eventHandle).GetAccessors(); + if (accessors.Adder == methodHandle || accessors.Remover == methodHandle || accessors.Raiser == methodHandle) + { + dependencies.Add( + factory.EventMetadata(new EventPseudoDesc((EcmaType)owningType, eventHandle)), + "Event associated with reflectable accessor"); + } + } + } } return dependencies; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index bdec43b6ce9873..5081a1b8ead9f7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -575,6 +575,16 @@ private void CreateNodeCaches() return new FieldMetadataNode(field); }); + _propertiesWithMetadata = new NodeCache(property => + { + return new PropertyMetadataNode(property); + }); + + _eventsWithMetadata = new NodeCache(@event => + { + return new EventMetadataNode(@event); + }); + _modulesWithMetadata = new NodeCache(module => { return new ModuleMetadataNode(module); @@ -1462,6 +1472,26 @@ internal FieldMetadataNode FieldMetadata(FieldDesc field) return _fieldsWithMetadata.GetOrAdd(field); } + private NodeCache _propertiesWithMetadata; + + internal PropertyMetadataNode PropertyMetadata(PropertyPseudoDesc property) + { + // These are only meaningful for UsageBasedMetadataManager. We should not have them + // in the dependency graph otherwise. + Debug.Assert(MetadataManager is UsageBasedMetadataManager); + return _propertiesWithMetadata.GetOrAdd(property); + } + + private NodeCache _eventsWithMetadata; + + internal EventMetadataNode EventMetadata(EventPseudoDesc @event) + { + // These are only meaningful for UsageBasedMetadataManager. We should not have them + // in the dependency graph otherwise. + Debug.Assert(MetadataManager is UsageBasedMetadataManager); + return _eventsWithMetadata.GetOrAdd(@event); + } + private NodeCache _modulesWithMetadata; internal ModuleMetadataNode ModuleMetadata(ModuleDesc module) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/PropertyMetadataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/PropertyMetadataNode.cs new file mode 100644 index 00000000000000..6a8fedbec05ee4 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/PropertyMetadataNode.cs @@ -0,0 +1,50 @@ +// 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 ILCompiler.DependencyAnalysisFramework; + +namespace ILCompiler.DependencyAnalysis +{ + /// + /// Represents a property that has metadata generated in the current compilation. + /// This corresponds to an ECMA-335 Property record. Unlike fields and methods, + /// properties are not first-class entities in the compiler's type system (there is no + /// interned PropertyDesc). + /// + /// + /// Only expected to be used during ILScanning when scanning for reflection. + /// + internal sealed class PropertyMetadataNode : DependencyNodeCore + { + private readonly PropertyPseudoDesc _property; + + public PropertyMetadataNode(PropertyPseudoDesc property) + { + _property = property; + } + + public PropertyPseudoDesc Property => _property; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) => null; + + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) + { + var dependencies = new List(); + CustomAttributeBasedDependencyAlgorithm.AddDependenciesDueToCustomAttributes(ref dependencies, factory, _property); + return dependencies; + } + + protected override string GetName(NodeFactory factory) + { + return "Property metadata: " + _property.ToString(); + } + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => true; + public override bool StaticDependenciesAreComputed => true; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index a0e5f77c7c533b..a03e3003f7417c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -494,7 +494,9 @@ + + diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributePropertyDataflow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributePropertyDataflow.cs index f87e1c8603a62c..b3c301044eafa8 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributePropertyDataflow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AttributePropertyDataflow.cs @@ -145,7 +145,6 @@ class AttributesOnProperty [Kept] [KeptAttributeAttribute(typeof(KeepsPublicMethodsAttribute))] [ExpectedWarning("IL2026", "--ClassWithKeptPublicMethods--")] - [UnexpectedWarning("IL2026", "--ClassWithKeptPublicMethods--", Tool.NativeAot, "https://github.com/dotnet/runtime/issues/120004")] [KeepsPublicMethods(Type = typeof(ClassWithKeptPublicMethods))] public static bool Property { [Kept] get; [Kept] set; } @@ -175,7 +174,6 @@ class AttributesOnEvent [KeptEventRemoveMethod] [KeptAttributeAttribute(typeof(KeepsPublicMethodsAttribute))] [ExpectedWarning("IL2026", "--ClassWithKeptPublicMethods--")] - [UnexpectedWarning("IL2026", "--ClassWithKeptPublicMethods--", Tool.NativeAot, "https://github.com/dotnet/runtime/issues/120004")] [KeepsPublicMethods(Type = typeof(ClassWithKeptPublicMethods))] public static event EventHandler Event_FieldSyntax; @@ -185,7 +183,6 @@ class AttributesOnEvent [KeptEventRemoveMethod] [KeptAttributeAttribute(typeof(KeepsPublicMethodsAttribute))] [ExpectedWarning("IL2026", "--ClassWithKeptPublicMethods--")] - [UnexpectedWarning("IL2026", "--ClassWithKeptPublicMethods--", Tool.NativeAot, "https://github.com/dotnet/runtime/issues/120004")] [KeepsPublicMethods(Type = typeof(ClassWithKeptPublicMethods))] public static event EventHandler Event_PropertySyntax { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnAttribute.cs index b22b50370af4fd..f6c92bf0113815 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnAttribute.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnAttribute.cs @@ -116,17 +116,11 @@ static void MethodWithAttributeWhichRequires() { } static int _fieldWithAttributeWhichRequires; [ExpectedWarning("IL2026", "--AttributeWhichRequiresAttribute.ctor--")] - [UnexpectedWarning("IL2026", "--AttributeWhichRequiresAttribute.ctor--", Tool.NativeAot, "https://github.com/dotnet/runtime/issues/120004")] [ExpectedWarning("IL3002", "--AttributeWhichRequiresAttribute.ctor--", Tool.Analyzer | Tool.NativeAot, "NativeAOT-specific warning")] - [UnexpectedWarning("IL3002", "--AttributeWhichRequiresAttribute.ctor--", Tool.NativeAot, "https://github.com/dotnet/runtime/issues/120004")] [ExpectedWarning("IL3050", "--AttributeWhichRequiresAttribute.ctor--", Tool.Analyzer | Tool.NativeAot, "NativeAOT-specific warning")] - [UnexpectedWarning("IL3050", "--AttributeWhichRequiresAttribute.ctor--", Tool.NativeAot, "https://github.com/dotnet/runtime/issues/120004")] [ExpectedWarning("IL2026", "--AttributeWhichRequiresOnPropertyAttribute.PropertyWhichRequires--")] - [UnexpectedWarning("IL2026", "--AttributeWhichRequiresOnPropertyAttribute.PropertyWhichRequires--", Tool.NativeAot, "https://github.com/dotnet/runtime/issues/120004")] [ExpectedWarning("IL3002", "--AttributeWhichRequiresOnPropertyAttribute.PropertyWhichRequires--", Tool.Analyzer | Tool.NativeAot, "NativeAOT-specific warning")] - [UnexpectedWarning("IL3002", "--AttributeWhichRequiresOnPropertyAttribute.PropertyWhichRequires--", Tool.NativeAot, "https://github.com/dotnet/runtime/issues/120004")] [ExpectedWarning("IL3050", "--AttributeWhichRequiresOnPropertyAttribute.PropertyWhichRequires--", Tool.Analyzer | Tool.NativeAot, "NativeAOT-specific warning")] - [UnexpectedWarning("IL3050", "--AttributeWhichRequiresOnPropertyAttribute.PropertyWhichRequires--", Tool.NativeAot, "https://github.com/dotnet/runtime/issues/120004")] [AttributeWhichRequires] [AttributeWhichRequiresOnProperty(PropertyWhichRequires = true)] static bool PropertyWithAttributeWhichRequires { get; set; }