Skip to content

Commit 8de49f0

Browse files
authored
Merge pull request #148 from servicetitan/fixArrayReverse
Fix critical bug with attribute reordering by destructive Array.Reverse() on memoized array
2 parents 51f71c0 + de75c81 commit 8de49f0

File tree

4 files changed

+29
-34
lines changed

4 files changed

+29
-34
lines changed

Orm/Xtensive.Orm/IoC/ServiceRegistration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public static ServiceRegistration[] CreateAll(Type type, bool defaultOnly) =>
8383
return Array.Empty<ServiceRegistration>();
8484

8585
var attributes = type.GetAttributes<ServiceAttribute>(AttributeSearchOptions.InheritNone);
86-
var registrations = new List<ServiceRegistration>(attributes.Length);
86+
var registrations = new List<ServiceRegistration>(attributes.Count);
8787
foreach (var sa in attributes) {
8888
if (!defaultOnly || sa.Default) {
8989
registrations.Add(new ServiceRegistration(sa.Type, sa.Name.IsNullOrEmpty() ? null : sa.Name, type, sa.Singleton));

Orm/Xtensive.Orm/Linq/ExpressionWriter.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2008-2020 Xtensive LLC.
1+
// Copyright (C) 2008-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Alexey Kochetov
@@ -294,12 +294,12 @@ protected virtual string GetTypeName(Type type)
294294
name = name.Replace('+', '.');
295295

296296
if (name.IndexOf("__DisplayClass", StringComparison.Ordinal) > 0 &&
297-
type.GetAttributes<CompilerGeneratedAttribute>(AttributeSearchOptions.InheritNone).Length > 0) {
297+
type.GetAttributes<CompilerGeneratedAttribute>(AttributeSearchOptions.InheritNone).Count > 0) {
298298
return "@";
299299
}
300300

301301
if (name.IndexOf("__AnonymousType", StringComparison.Ordinal) > 0 &&
302-
type.GetAttributes<CompilerGeneratedAttribute>(AttributeSearchOptions.InheritNone).Length > 0) {
302+
type.GetAttributes<CompilerGeneratedAttribute>(AttributeSearchOptions.InheritNone).Count > 0) {
303303
return string.Format("@<{0}>",
304304
(from pi in type.GetProperties() select pi.Name).ToCommaDelimitedString());
305305
}
@@ -340,7 +340,7 @@ protected override Expression VisitConstant(ConstantExpression c)
340340
{
341341
var type = c.Type;
342342
if (type.Name.IndexOf("__DisplayClass", StringComparison.Ordinal) > 0 &&
343-
type.GetAttributes<CompilerGeneratedAttribute>(AttributeSearchOptions.InheritNone).Length > 0) {
343+
type.GetAttributes<CompilerGeneratedAttribute>(AttributeSearchOptions.InheritNone).Count > 0) {
344344
// A constant of display class type
345345
Write("@");
346346
}
@@ -556,7 +556,7 @@ protected override Expression VisitMethodCall(MethodCallExpression mc)
556556
}
557557
else {
558558
// Static method
559-
if (mc.Method.GetAttributes<ExtensionAttribute>(AttributeSearchOptions.InheritNone).Length > 0) {
559+
if (mc.Method.GetAttributes<ExtensionAttribute>(AttributeSearchOptions.InheritNone).Count > 0) {
560560
// A special case: extension method
561561
Visit(mc.Arguments[0]);
562562
arguments = new System.Collections.ObjectModel.ReadOnlyCollection<Expression>(mc.Arguments.Skip(1).ToList());

Orm/Xtensive.Orm/Orm/Building/Builders/ModelDefBuilder.cs

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (C) 2009-2020 Xtensive LLC.
1+
// Copyright (C) 2009-2021 Xtensive LLC.
22
// This code is distributed under MIT license terms.
33
// See the License.txt file in the project root for more information.
44
// Created by: Dmitri Maximov
@@ -151,12 +151,12 @@ public void ProcessProperties(TypeDef typeDef, HierarchyDef hierarchyDef)
151151
}
152152

153153
// FieldAttribute presence is required
154-
var fieldAttributes = GetFieldAttributes<FieldAttribute>(propertyInfo);
155-
if (fieldAttributes.Length == 0) {
154+
var reversedFieldAttributes = GetReversedFieldAttributes<FieldAttribute>(propertyInfo);
155+
if (reversedFieldAttributes.Count == 0) {
156156
continue;
157157
}
158158

159-
var field = DefineField(propertyInfo, fieldAttributes);
159+
var field = DefineField(propertyInfo, reversedFieldAttributes);
160160

161161
// Declared & inherited fields must be processed for hierarchy root
162162
if (hierarchyDef != null) {
@@ -284,9 +284,9 @@ public HierarchyDef DefineHierarchy(TypeDef typeDef, HierarchyRootAttribute attr
284284
}
285285

286286
public FieldDef DefineField(PropertyInfo propertyInfo) =>
287-
DefineField(propertyInfo, GetFieldAttributes<FieldAttribute>(propertyInfo));
287+
DefineField(propertyInfo, GetReversedFieldAttributes<FieldAttribute>(propertyInfo));
288288

289-
public FieldDef DefineField(PropertyInfo propertyInfo, FieldAttribute[] fieldAttributes)
289+
public FieldDef DefineField(PropertyInfo propertyInfo, IReadOnlyList<FieldAttribute> reversedFieldAttributes)
290290
{
291291
// Persistent indexers are not supported
292292
var indexParameters = propertyInfo.GetIndexParameters();
@@ -297,21 +297,21 @@ public FieldDef DefineField(PropertyInfo propertyInfo, FieldAttribute[] fieldAtt
297297
var fieldDef = new FieldDef(propertyInfo, context.Validator);
298298
fieldDef.Name = context.NameBuilder.BuildFieldName(fieldDef);
299299

300-
if (fieldAttributes.Length > 0) {
301-
foreach (var attribute in fieldAttributes) {
302-
attributeProcessor.Process(fieldDef, attribute);
300+
if (reversedFieldAttributes.Count > 0) {
301+
for (int i = reversedFieldAttributes.Count; i-- > 0;) {
302+
attributeProcessor.Process(fieldDef, reversedFieldAttributes[i]);
303303
}
304304

305305
// Association
306-
var associationAttributes = GetFieldAttributes<AssociationAttribute>(propertyInfo);
307-
foreach (var attribute in associationAttributes) {
308-
attributeProcessor.Process(fieldDef, attribute);
306+
var reversedAssociationAttributes = GetReversedFieldAttributes<AssociationAttribute>(propertyInfo);
307+
for (int i = reversedAssociationAttributes.Count; i-- > 0;) {
308+
attributeProcessor.Process(fieldDef, reversedAssociationAttributes[i]);
309309
}
310310

311311
// Mapping name
312-
var mappingAttributes = GetFieldAttributes<FieldMappingAttribute>(propertyInfo);
313-
foreach (var attribute in mappingAttributes) {
314-
attributeProcessor.Process(fieldDef, attribute);
312+
var reversedMappingAttributes = GetReversedFieldAttributes<FieldMappingAttribute>(propertyInfo);
313+
for (int i = reversedMappingAttributes.Count; i-- > 0;) {
314+
attributeProcessor.Process(fieldDef, reversedMappingAttributes[i]);
315315
}
316316

317317
// Type discriminator
@@ -370,16 +370,11 @@ private bool ShouldSkipFulltextIndex(TypeDef typeDef)
370370
return hierarchy == null && !typeDef.IsStructure;
371371
}
372372

373-
private static T[] GetFieldAttributes<T>(PropertyInfo property)
374-
where T : Attribute
375-
{
376-
var attributes = property.GetAttributes<T>(AttributeSearchOptions.InheritAll);
377-
// Attributes will contain attributes from all inheritance chain
378-
// with the most specific type first.
379-
// Reverse them for correct processing (i.e. descendants override settings from base).
380-
Array.Reverse(attributes);
381-
return attributes;
382-
}
373+
// Attributes will contain attributes from all inheritance chain
374+
// with the most specific type first.
375+
// Should be enumerated in reversed order for correct processing (i.e. descendants override settings from base).
376+
private static IReadOnlyList<T> GetReversedFieldAttributes<T>(PropertyInfo property) where T : Attribute =>
377+
property.GetAttributes<T>(AttributeSearchOptions.InheritAll);
383378

384379
private bool IsTypeAvailable(Type type) =>
385380
context.BuilderConfiguration.ModelFilter.IsTypeAvailable(type)

Orm/Xtensive.Orm/Reflection/AttributeHelper.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ private static readonly ConcurrentDictionary<AttributesKey, Attribute[]> attribu
3636
/// <param name="options">Attribute search options.</param>
3737
/// <returns>An array of attributes of specified type.</returns>
3838
///
39-
public static TAttribute[] GetAttributes<TAttribute>(this MemberInfo member, AttributeSearchOptions options = AttributeSearchOptions.InheritNone)
39+
public static IReadOnlyList<TAttribute> GetAttributes<TAttribute>(this MemberInfo member, AttributeSearchOptions options = AttributeSearchOptions.InheritNone)
4040
where TAttribute : Attribute =>
4141
AttributeDictionary<TAttribute>.Dictionary.GetOrAdd(
4242
new PerAttributeKey(member, options),
@@ -59,7 +59,7 @@ public static TAttribute GetAttribute<TAttribute>(this MemberInfo member, Attrib
5959
where TAttribute : Attribute
6060
{
6161
var attributes = member.GetAttributes<TAttribute>(options);
62-
return attributes.Length switch {
62+
return attributes.Count switch {
6363
0 => null,
6464
1 => attributes[0],
6565
_ => throw new InvalidOperationException(string.Format(Strings.ExMultipleAttributesOfTypeXAreNotAllowedHere,
@@ -68,7 +68,7 @@ public static TAttribute GetAttribute<TAttribute>(this MemberInfo member, Attrib
6868
};
6969
}
7070

71-
private static Attribute[] GetAttributes(MemberInfo member, Type attributeType, AttributeSearchOptions options) =>
71+
private static IReadOnlyList<Attribute> GetAttributes(MemberInfo member, Type attributeType, AttributeSearchOptions options) =>
7272
attributesByMemberInfoAndSearchOptions.GetOrAdd(
7373
new AttributesKey(member, attributeType, options),
7474
t => ExtractAttributes(t).ToArray()

0 commit comments

Comments
 (0)