Skip to content

Commit fe178fa

Browse files
committed
[mono] Fix GetCustomAttributes API with a custom attribute provider
1 parent 4f3bae5 commit fe178fa

File tree

2 files changed

+115
-7
lines changed

2 files changed

+115
-7
lines changed

src/libraries/System.Runtime/tests/System.Reflection.Tests/CustomAttributeTests.cs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,5 +129,113 @@ public void StringAttributeValueRefEqualsStringEmpty () {
129129

130130
Assert.Same(string.Empty, attr.NamedField);
131131
}
132+
133+
[AttributeUsage(AttributeTargets.Parameter)]
134+
internal class MyParameterAttribute : Attribute {}
135+
136+
[AttributeUsage(AttributeTargets.Property)]
137+
internal class MyPropertyAttribute : Attribute {}
138+
139+
internal sealed class PropertyAsParameterInfo : ParameterInfo
140+
{
141+
private readonly PropertyInfo _underlyingProperty;
142+
private readonly ParameterInfo? _constructionParameterInfo;
143+
144+
public PropertyAsParameterInfo(PropertyInfo property, ParameterInfo parameterInfo)
145+
{
146+
_underlyingProperty = property;
147+
_constructionParameterInfo = parameterInfo;
148+
MemberImpl = _underlyingProperty;
149+
}
150+
151+
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
152+
{
153+
var constructorAttributes = _constructionParameterInfo?.GetCustomAttributes(attributeType, inherit);
154+
155+
if (constructorAttributes == null || constructorAttributes is { Length: 0 })
156+
{
157+
return _underlyingProperty.GetCustomAttributes(attributeType, inherit);
158+
}
159+
160+
var propertyAttributes = _underlyingProperty.GetCustomAttributes(attributeType, inherit);
161+
162+
var mergedAttributes = new Attribute[constructorAttributes.Length + propertyAttributes.Length];
163+
Array.Copy(constructorAttributes, mergedAttributes, constructorAttributes.Length);
164+
Array.Copy(propertyAttributes, 0, mergedAttributes, constructorAttributes.Length, propertyAttributes.Length);
165+
166+
return mergedAttributes;
167+
}
168+
169+
public override object[] GetCustomAttributes(bool inherit)
170+
{
171+
var constructorAttributes = _constructionParameterInfo?.GetCustomAttributes(inherit);
172+
173+
if (constructorAttributes == null || constructorAttributes is { Length: 0 })
174+
{
175+
return _underlyingProperty.GetCustomAttributes(inherit);
176+
}
177+
178+
var propertyAttributes = _underlyingProperty.GetCustomAttributes(inherit);
179+
180+
var mergedAttributes = new object[constructorAttributes.Length + propertyAttributes.Length];
181+
Array.Copy(constructorAttributes, mergedAttributes, constructorAttributes.Length);
182+
Array.Copy(propertyAttributes, 0, mergedAttributes, constructorAttributes.Length, propertyAttributes.Length);
183+
184+
return mergedAttributes;
185+
}
186+
187+
public override IList<CustomAttributeData> GetCustomAttributesData()
188+
{
189+
var attributes = new List<CustomAttributeData>(
190+
_constructionParameterInfo?.GetCustomAttributesData() ?? Array.Empty<CustomAttributeData>());
191+
attributes.AddRange(_underlyingProperty.GetCustomAttributesData());
192+
193+
return attributes.AsReadOnly();
194+
}
195+
}
196+
197+
internal class CustomAttributeProviderTestClass
198+
{
199+
public CustomAttributeProviderTestClass([MyParameter] int integerProperty)
200+
{
201+
IntegerProperty = integerProperty;
202+
}
203+
204+
[MyProperty]
205+
public int IntegerProperty { get; set; }
206+
}
207+
208+
[Fact]
209+
public void CustomAttributeProvider ()
210+
{
211+
var type = typeof(CustomAttributeProviderTestClass);
212+
var propertyInfo = type.GetProperty(nameof(CustomAttributeProviderTestClass.IntegerProperty));
213+
var ctorInfo = type.GetConstructor(new Type[] { typeof(int) });
214+
var ctorParamInfo = ctorInfo.GetParameters()[0];
215+
var propertyAndParamInfo = new PropertyAsParameterInfo(propertyInfo, ctorParamInfo);
216+
217+
// check GetCustomAttribute API
218+
var cattrObjects = propertyAndParamInfo.GetCustomAttributes(true);
219+
Assert.Equal(2, cattrObjects.Length);
220+
Assert.Equal(typeof(MyParameterAttribute), cattrObjects[0].GetType());
221+
Assert.Equal(typeof(MyPropertyAttribute), cattrObjects[1].GetType());
222+
223+
cattrObjects = propertyAndParamInfo.GetCustomAttributes(typeof(Attribute), true);
224+
Assert.Equal(2, cattrObjects.Length);
225+
Assert.Equal(typeof(MyParameterAttribute), cattrObjects[0].GetType());
226+
Assert.Equal(typeof(MyPropertyAttribute), cattrObjects[1].GetType());
227+
228+
var cattrsEnumerable = propertyAndParamInfo.GetCustomAttributes();
229+
Attribute[] cattrs = cattrsEnumerable.Cast<Attribute>().ToArray();
230+
Assert.Equal(2, cattrs.Length);
231+
Assert.Equal(typeof(MyParameterAttribute), cattrs[0].GetType());
232+
Assert.Equal(typeof(MyPropertyAttribute), cattrs[1].GetType());
233+
234+
// check GetCustomAttributeData API
235+
var customAttributesData = propertyAndParamInfo.GetCustomAttributesData();
236+
Assert.Equal(2, customAttributesData.Count);
237+
Assert.Equal(typeof(MyParameterAttribute), customAttributesData[0].AttributeType);
238+
Assert.Equal(typeof(MyPropertyAttribute), customAttributesData[1].AttributeType);
239+
}
132240
}
133241
}

src/mono/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,7 @@ private static bool IsUserCattrProvider(object obj)
114114
// FIXME: Callers are explicitly passing in null for attributeType, but GetCustomAttributes prohibits null attributeType arguments
115115
internal static object[] GetCustomAttributesBase(ICustomAttributeProvider obj, Type? attributeType, bool inheritedOnly)
116116
{
117-
object[] attrs;
118-
119-
if (IsUserCattrProvider(obj))
120-
attrs = obj.GetCustomAttributes(attributeType!, true);
121-
else
122-
attrs = GetCustomAttributesInternal(obj, attributeType!, false);
123-
117+
object[] attrs = GetCustomAttributesInternal(obj, attributeType!, pseudoAttrs: false);
124118
//
125119
// All pseudo custom attributes are Inherited = false hence we can avoid
126120
// building attributes array which would be discarded by inherited checks
@@ -156,6 +150,9 @@ internal static object[] GetCustomAttributes(ICustomAttributeProvider obj, Type
156150
&& attributeType != typeof(Attribute) && attributeType != typeof(CustomAttribute) && attributeType != typeof(object))
157151
throw new ArgumentException(SR.Argument_MustHaveAttributeBaseClass + " " + attributeType.FullName);
158152

153+
if (IsUserCattrProvider(obj))
154+
return obj.GetCustomAttributes(attributeType, inherit);
155+
159156
// FIXME: GetCustomAttributesBase doesn't like being passed a null attributeType
160157
if (attributeType == typeof(CustomAttribute))
161158
attributeType = null!;
@@ -306,6 +303,9 @@ internal static object[] GetCustomAttributes(ICustomAttributeProvider obj, bool
306303
{
307304
ArgumentNullException.ThrowIfNull(obj);
308305

306+
if (IsUserCattrProvider(obj))
307+
return obj.GetCustomAttributes(typeof(Attribute), inherit);
308+
309309
if (!inherit)
310310
return (object[])GetCustomAttributesBase(obj, null, false).Clone();
311311

0 commit comments

Comments
 (0)