diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs index b0101c75e3df1a..a75cea46857553 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs @@ -1573,19 +1573,42 @@ private static void AddCustomAttributes( } } - RuntimePropertyInfo? property = (RuntimePropertyInfo?)(type is null ? - attributeType.GetProperty(name) : - attributeType.GetProperty(name, type, [])) ?? - throw new CustomAttributeFormatException(SR.Format(SR.RFLCT_InvalidPropFail, name)); - RuntimeMethodInfo setMethod = property.GetSetMethod(true)!; - - // Public properties may have non-public setter methods - if (!setMethod.IsPublic) + RuntimeMethodInfo? setMethod = null; + RuntimeMethodInfo? getMethod = null; + Type baseAttributeType = attributeType; + + for (; ; ) { - continue; - } + RuntimePropertyInfo? property = (RuntimePropertyInfo?)(type is null ? + baseAttributeType.GetProperty(name) : + baseAttributeType.GetProperty(name, type, [])); + + if (property is not null) + { + setMethod = property.GetSetMethod(true); + getMethod = property.GetGetMethod(true); + + if (setMethod is not null) + { + // Public properties may have non-public setter methods + if (setMethod.IsPublic) + { + setMethod.InvokePropertySetter(attribute, BindingFlags.Default, null, value, null); + } + + break; + } + } + else + { + setMethod = null; + getMethod = null; + } - setMethod.InvokePropertySetter(attribute, BindingFlags.Default, null, value, null); + baseAttributeType = baseAttributeType.BaseType is null || (getMethod is not null && !getMethod.IsVirtual) + ? throw new CustomAttributeFormatException(SR.Format(SR.RFLCT_InvalidPropFail, name)) + : baseAttributeType.BaseType; + } } else { diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInstantiator.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInstantiator.cs index 99b697d56c264a..4dce71374eab94 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInstantiator.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Extensions/NonPortable/CustomAttributeInstantiator.cs @@ -104,16 +104,37 @@ public static Attribute Instantiate(this CustomAttributeData cad) else { // Property + MethodInfo? getMethod = null; + MethodInfo? setMethod = null; + for (; ; ) { PropertyInfo? propertyInfo = walk.GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); - if (propertyInfo != null) + + if (propertyInfo is not null) { - propertyInfo.SetValue(newAttribute, argumentValue); - break; + getMethod = propertyInfo.GetGetMethod(true); + setMethod = propertyInfo.GetSetMethod(true); + + if (setMethod is not null) + { + // Public properties may have non-public setter methods + if (setMethod.IsPublic) + { + propertyInfo.SetValue(newAttribute, argumentValue); + } + + break; + } } + else + { + getMethod = null; + setMethod = null; + } + Type? baseType = walk.BaseType; - if (baseType == null) + if (baseType == null || (getMethod is not null && !getMethod.IsVirtual)) throw new CustomAttributeFormatException(SR.Format(SR.RFLCT_InvalidPropFail, name)); walk = baseType; } 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 a83ad7506418b8..40b2bdcc0b9bf7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs @@ -240,6 +240,7 @@ private static bool AddDependenciesFromPropertySetter(DependencyList dependencie MetadataReader reader = attributeTypeDefinition.MetadataReader; var typeDefinition = reader.GetTypeDefinition(attributeTypeDefinition.Handle); + MethodDesc getterMethod = null; foreach (PropertyDefinitionHandle propDefHandle in typeDefinition.GetProperties()) { @@ -263,6 +264,11 @@ private static bool AddDependenciesFromPropertySetter(DependencyList dependencie dependencies.Add(factory.ReflectedMethod(setterMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)), "Custom attribute blob"); } + if (!accessors.Getter.IsNil) + { + getterMethod = (MethodDesc)attributeTypeDefinition.EcmaModule.GetObject(accessors.Getter); + } + return true; } } @@ -270,7 +276,7 @@ private static bool AddDependenciesFromPropertySetter(DependencyList dependencie // Haven't found it in current type. Check the base type. TypeDesc baseType = attributeType.BaseType; - if (baseType != null) + if (baseType != null && (getterMethod is null || getterMethod.IsVirtual)) return AddDependenciesFromPropertySetter(dependencies, factory, baseType, propertyName); // Not found. This is bad metadata that will result in a runtime failure, but we shouldn't fail the compilation. diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Attributes.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Attributes.cs index 3784d52cfcaa0b..986e027e0e3e7b 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Attributes.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Attributes.cs @@ -316,6 +316,15 @@ public static void GetCustomAttributesGenericAttributeHasValues() () => Assert.Equal(4, attribute.OptionalValue)); } + [Fact] + public static void GetCustomAttributesWithSettersDefinedOnBaseClass() + { + object[] attributes = typeof(ClassWithDerivedAttr).GetCustomAttributes(true); + object attribute = Assert.Single(attributes); + var derivedAttributeWithGetterAttr = Assert.IsType(attribute); + Assert.Equal(2, derivedAttributeWithGetterAttr.P); + } + private static void GenericAttributesTestHelper(Func getCustomAttributes) { Attribute[] openGenericAttributes = getCustomAttributes(typeof(GenericAttribute<>)); @@ -326,6 +335,32 @@ private static void GenericAttributesTestHelper(Func Assert.NotEmpty(closedGenericAttributes), () => Assert.All(closedGenericAttributes, a => Assert.IsType>(a))); } + + public class BaseAttributeWithGetterSetter : Attribute + { + protected int _p; + + public virtual int P + { + get => _p; + set + { + _p = value; + } + } + } + + public class DerivedAttributeWithGetter : BaseAttributeWithGetterSetter + { + public override int P + { + get => _p; + } + } + + [DerivedAttributeWithGetter(P = 2)] + public class ClassWithDerivedAttr + { } } public static class GetCustomAttribute diff --git a/src/mono/mono/metadata/custom-attrs.c b/src/mono/mono/metadata/custom-attrs.c index 8bb0fb414a235d..3d18d5f633ec6b 100644 --- a/src/mono/mono/metadata/custom-attrs.c +++ b/src/mono/mono/metadata/custom-attrs.c @@ -1245,21 +1245,47 @@ create_custom_attr (MonoImage *image, MonoMethod *method, const guchar *data, gu mono_field_set_value_internal (MONO_HANDLE_RAW (attr), field, val); // FIXMEcoop } else if (named_type == CATTR_TYPE_PROPERTY) { - MonoProperty *prop; - prop = mono_class_get_property_from_name_internal (mono_handle_class (attr), name); - if (!prop) { + MonoProperty *prop = NULL; + MonoProperty *firstNotNullPropInHierarchy = NULL; + MonoMethod *setMethod = NULL; + MonoMethod *getMethod = NULL; + MonoClass *attrBaseClass = mono_handle_class (attr); + while (!setMethod && attrBaseClass && (!prop || !getMethod || m_method_is_virtual(getMethod))) + { + prop = mono_class_get_property_from_name_internal (attrBaseClass, name); + + if (prop) + { + if (!firstNotNullPropInHierarchy) + { + firstNotNullPropInHierarchy = prop; + } + + setMethod = prop->set; + getMethod = prop->get; + } + else + { + setMethod = NULL; + getMethod = NULL; + } + + attrBaseClass = m_class_get_parent(attrBaseClass); + } + + if (!firstNotNullPropInHierarchy) { mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Could not find a property with name %s", name); goto fail; } - if (!prop->set) { + if (!setMethod) { mono_error_set_generic_error (error, "System.Reflection", "CustomAttributeFormatException", "Could not find the setter for %s", name); goto fail; } /* can we have more that 1 arg in a custom attr named property? */ prop_type = prop->get? mono_method_signature_internal (prop->get)->ret : - mono_method_signature_internal (prop->set)->params [mono_method_signature_internal (prop->set)->param_count - 1]; + mono_method_signature_internal (setMethod)->params [mono_method_signature_internal (setMethod)->param_count - 1]; MonoObject *param_obj; pparams [0] = load_cattr_value (image, prop_type, ¶m_obj, named, data_end, &named, error); diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 9ae7f7b9cea5c9..53efb80894e794 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -1317,16 +1317,39 @@ protected void MarkCustomAttributeProperties(ICustomAttribute ca, TypeDefinition protected void MarkCustomAttributeProperty(CustomAttributeNamedArgument namedArgument, TypeDefinition attribute, ICustomAttribute ca, in DependencyInfo reason, MessageOrigin origin) { - PropertyDefinition? property = GetProperty(attribute, namedArgument.Name); - if (property != null) - MarkMethod(property.SetMethod, reason, origin); + TypeDefinition? type = attribute; + MethodDefinition? setMethod = null; + MethodDefinition? getMethod = null; + PropertyDefinition? property = null; - MarkCustomAttributeArgument(namedArgument.Argument, ca, origin); + while (setMethod is null && type is not null && (property is null || getMethod is null || getMethod.IsVirtual)) + { + property = type.Properties.FirstOrDefault(p => p.Name == namedArgument.Name); + + if (property is not null) + { + setMethod = property.SetMethod; + getMethod = property.GetMethod; + } + else + { + setMethod = null; + getMethod = null; + } + + type = Context.TryResolve(type.BaseType); + } - if (property != null && Annotations.FlowAnnotations.RequiresDataFlowAnalysis(property.SetMethod)) + if (setMethod is not null) { - var scanner = new AttributeDataFlow(Context, this, origin); - scanner.ProcessAttributeDataflow(property.SetMethod, new List { namedArgument.Argument }); + MarkMethod(setMethod, reason, origin); + MarkCustomAttributeArgument(namedArgument.Argument, ca, origin); + + if (Annotations.FlowAnnotations.RequiresDataFlowAnalysis(setMethod)) + { + var scanner = new AttributeDataFlow(Context, this, origin); + scanner.ProcessAttributeDataflow(setMethod, new List { namedArgument.Argument }); + } } }