0ff56463394d10cd33b29ccfc1524edc2cc68e47
[platform/upstream/dotnet/runtime.git] /
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Reflection;
7 using System.Reflection.Context;
8 using System.Threading;
9
10 namespace System.ComponentModel.Composition.Registration
11 {
12     public class RegistrationBuilder : CustomReflectionContext
13     {
14         internal sealed class InnerRC : ReflectionContext
15         {
16             public override TypeInfo MapType(TypeInfo t) { return t; }
17             public override Assembly MapAssembly(Assembly a) { return a; }
18         }
19
20         private static readonly ReflectionContext s_inner = new InnerRC();
21         private static readonly List<object> s_emptyList = new List<object>();
22
23         private readonly Lock _lock = new Lock();
24         private readonly List<PartBuilder> _conventions = new List<PartBuilder>();
25
26         private readonly Dictionary<MemberInfo, List<Attribute>> _memberInfos = new Dictionary<MemberInfo, List<Attribute>>();
27         private readonly Dictionary<ParameterInfo, List<Attribute>> _parameters = new Dictionary<ParameterInfo, List<Attribute>>();
28
29         public RegistrationBuilder() : base(s_inner)
30         {
31         }
32
33         public PartBuilder<T> ForTypesDerivedFrom<T>()
34         {
35             var partBuilder = new PartBuilder<T>((t) => typeof(T) != t && typeof(T).IsAssignableFrom(t));
36             _conventions.Add(partBuilder);
37
38             return partBuilder;
39         }
40
41         public PartBuilder ForTypesDerivedFrom(Type type)
42         {
43             if (type is null)
44             {
45                 throw new ArgumentNullException(nameof(type));
46             }
47
48             var partBuilder = new PartBuilder((t) => type != t && type.IsAssignableFrom(t));
49             _conventions.Add(partBuilder);
50
51             return partBuilder;
52         }
53
54         public PartBuilder<T> ForType<T>()
55         {
56             var partBuilder = new PartBuilder<T>((t) => t == typeof(T));
57             _conventions.Add(partBuilder);
58
59             return partBuilder;
60         }
61
62         public PartBuilder ForType(Type type)
63         {
64             if (type is null)
65             {
66                 throw new ArgumentNullException(nameof(type));
67             }
68
69             var partBuilder = new PartBuilder((t) => t == type);
70             _conventions.Add(partBuilder);
71
72             return partBuilder;
73         }
74
75         public PartBuilder<T> ForTypesMatching<T>(Predicate<Type> typeFilter)
76         {
77             if (typeFilter is null)
78             {
79                 throw new ArgumentNullException(nameof(typeFilter));
80             }
81
82             var partBuilder = new PartBuilder<T>(typeFilter);
83             _conventions.Add(partBuilder);
84
85             return partBuilder;
86         }
87
88         public PartBuilder ForTypesMatching(Predicate<Type> typeFilter)
89         {
90             if (typeFilter is null)
91             {
92                 throw new ArgumentNullException(nameof(typeFilter));
93             }
94
95             var partBuilder = new PartBuilder(typeFilter);
96             _conventions.Add(partBuilder);
97
98             return partBuilder;
99         }
100
101         private IEnumerable<Tuple<object, List<Attribute>>> EvaluateThisTypeAgainstTheConvention(Type type)
102         {
103             List<Attribute> attributes = new List<Attribute>();
104
105             var configuredMembers = new List<Tuple<object, List<Attribute>>>();
106             bool specifiedConstructor = false;
107             bool matchedConvention = false;
108
109             foreach (PartBuilder builder in _conventions.Where(c => c.SelectType(type.UnderlyingSystemType)))
110             {
111                 attributes.AddRange(builder.BuildTypeAttributes(type));
112
113                 specifiedConstructor |= builder.BuildConstructorAttributes(type, ref configuredMembers);
114                 builder.BuildPropertyAttributes(type, ref configuredMembers);
115                 matchedConvention = true;
116             }
117
118             if (matchedConvention && !specifiedConstructor)
119             {
120                 // DefaultConstructor
121                 PartBuilder.BuildDefaultConstructorAttributes(type, ref configuredMembers);
122             }
123
124             configuredMembers.Add(Tuple.Create((object)type, attributes));
125
126             return configuredMembers;
127         }
128
129         // Handle Type Exports and Parts
130         protected override IEnumerable<object> GetCustomAttributes(System.Reflection.MemberInfo member, IEnumerable<object> declaredAttributes)
131         {
132             IEnumerable<object> attributes = base.GetCustomAttributes(member, declaredAttributes);
133
134             // Now edit the attributes returned from the base type
135             List<Attribute> cachedAttributes = null;
136
137             if (member.MemberType == MemberTypes.TypeInfo || member.MemberType == MemberTypes.NestedType)
138             {
139                 MemberInfo underlyingMemberType = ((Type)member).UnderlyingSystemType;
140                 using (new ReadLock(_lock))
141                 {
142                     _memberInfos.TryGetValue(underlyingMemberType, out cachedAttributes);
143                 }
144
145                 if (cachedAttributes == null)
146                 {
147                     using (new WriteLock(_lock))
148                     {
149                         //Double check locking another thread may have inserted one while we were away.
150                         if (!_memberInfos.TryGetValue(underlyingMemberType, out cachedAttributes))
151                         {
152                             List<Attribute> attributeList;
153                             foreach (Tuple<object, List<Attribute>> element in EvaluateThisTypeAgainstTheConvention((Type)member))
154                             {
155                                 attributeList = element.Item2;
156                                 if (attributeList != null)
157                                 {
158                                     if (element.Item1 is MemberInfo)
159                                     {
160                                         List<Attribute> memberAttributes;
161                                         switch (((MemberInfo)element.Item1).MemberType)
162                                         {
163                                             case MemberTypes.Constructor:
164                                                 if (!_memberInfos.TryGetValue((MemberInfo)element.Item1, out memberAttributes))
165                                                 {
166                                                     _memberInfos.Add((MemberInfo)element.Item1, element.Item2);
167                                                 }
168                                                 else
169                                                 {
170                                                     memberAttributes.AddRange(attributeList);
171                                                 }
172                                                 break;
173                                             case MemberTypes.TypeInfo:
174                                             case MemberTypes.NestedType:
175                                             case MemberTypes.Property:
176                                                 if (!_memberInfos.TryGetValue((MemberInfo)element.Item1, out memberAttributes))
177                                                 {
178                                                     _memberInfos.Add((MemberInfo)element.Item1, element.Item2);
179                                                 }
180                                                 else
181                                                 {
182                                                     memberAttributes.AddRange(attributeList);
183                                                 }
184                                                 break;
185                                             default:
186                                                 break;
187                                         }
188                                     }
189                                     else
190                                     {
191                                         if (!(element.Item1 is ParameterInfo))
192                                             throw new Exception(SR.Diagnostic_InternalExceptionMessage);
193                                         // Item contains as Constructor parameter to configure
194                                         if (!_parameters.TryGetValue((ParameterInfo)element.Item1, out List<Attribute> parameterAttributes))
195                                         {
196                                             _parameters.Add((ParameterInfo)element.Item1, element.Item2);
197                                         }
198                                         else
199                                         {
200                                             parameterAttributes.AddRange(cachedAttributes);
201                                         }
202                                     }
203                                 }
204                             }
205                         }
206
207                         // We will have updated all of the MemberInfos by now so lets reload cachedAttributes wiuth the current store
208                         _memberInfos.TryGetValue(underlyingMemberType, out cachedAttributes);
209                     }
210                 }
211             }
212             else if (member.MemberType == System.Reflection.MemberTypes.Constructor || member.MemberType == System.Reflection.MemberTypes.Property)
213             {
214                 cachedAttributes = ReadMemberCustomAttributes(member);
215             }
216
217             return cachedAttributes == null ? attributes : attributes.Concat(cachedAttributes);
218         }
219
220         //This is where ParameterImports will be handled
221         protected override IEnumerable<object> GetCustomAttributes(System.Reflection.ParameterInfo parameter, IEnumerable<object> declaredAttributes)
222         {
223             IEnumerable<object> attributes = base.GetCustomAttributes(parameter, declaredAttributes);
224             List<Attribute> cachedAttributes = ReadParameterCustomAttributes(parameter);
225
226             return cachedAttributes == null ? attributes : attributes.Concat(cachedAttributes);
227         }
228
229         private List<Attribute> ReadMemberCustomAttributes(MemberInfo member)
230         {
231             List<Attribute> cachedAttributes = null;
232             bool getMemberAttributes = false;
233
234             // Now edit the attributes returned from the base type
235             using (new ReadLock(_lock))
236             {
237                 if (!_memberInfos.TryGetValue(member, out cachedAttributes))
238                 {
239                     // If there is nothing for this member Cache any attributes for the DeclaringType
240                     if (!_memberInfos.TryGetValue(member.DeclaringType.UnderlyingSystemType, out cachedAttributes))
241                     {
242                         // If there is nothing for this parameter look to see if the declaring Member has been cached yet?
243                         // need to do it outside of the lock, so set the flag we'll check it in a bit
244                         getMemberAttributes = true;
245                     }
246
247                     cachedAttributes = null;
248                 }
249             }
250
251             if (getMemberAttributes)
252             {
253                 GetCustomAttributes(member.DeclaringType, s_emptyList);
254
255                 // We should have run the rules for the enclosing parameter so we can again
256                 using (new ReadLock(_lock))
257                 {
258                     _memberInfos.TryGetValue(member, out cachedAttributes);
259                 }
260             }
261
262             return cachedAttributes;
263         }
264
265         private List<Attribute> ReadParameterCustomAttributes(ParameterInfo parameter)
266         {
267             List<Attribute> cachedAttributes = null;
268             bool getMemberAttributes = false;
269
270             // Now edit the attributes returned from the base type
271             using (new ReadLock(_lock))
272             {
273                 if (!_parameters.TryGetValue(parameter, out cachedAttributes))
274                 {
275                     // If there is nothing for this parameter Cache any attributes for the DeclaringType
276                     if (!_memberInfos.TryGetValue(parameter.Member.DeclaringType, out cachedAttributes))
277                     {
278                         // If there is nothing for this parameter look to see if the declaring Member has been cached yet?
279                         // need to do it outside of the lock, so set the flag we'll check it in a bit
280                         getMemberAttributes = true;
281                     }
282                     cachedAttributes = null;
283                 }
284             }
285
286             if (getMemberAttributes)
287             {
288                 GetCustomAttributes(parameter.Member.DeclaringType, s_emptyList);
289
290                 // We should have run the rules for the enclosing parameter so we can again
291                 using (new ReadLock(_lock))
292                 {
293                     _parameters.TryGetValue(parameter, out cachedAttributes);
294                 }
295             }
296
297             return cachedAttributes;
298         }
299     }
300 }