206483b25db40061e8ab4abaac6edabc17296bea
[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.Numerics.Hashing;
7 using System.Reflection;
8 using System.Linq.Expressions;
9 using System.Diagnostics;
10 using System.Composition.Debugging;
11 using System.Composition.TypedParts.ActivationFeatures;
12 using System.Composition.Hosting.Core;
13 using System.Composition.Convention;
14 using System.Composition.Hosting;
15
16 namespace System.Composition.TypedParts.Discovery
17 {
18     [DebuggerDisplay("{PartType.Name}")]
19     [DebuggerTypeProxy(typeof(DiscoveredPartDebuggerProxy))]
20     internal sealed class DiscoveredPart
21     {
22         private readonly TypeInfo _partType;
23         private readonly AttributedModelProvider _attributeContext;
24         private readonly ICollection<DiscoveredExport> _exports = new List<DiscoveredExport>();
25         private readonly ActivationFeature[] _activationFeatures;
26         private readonly Lazy<IDictionary<string, object>> _partMetadata;
27
28         // This is unbounded so potentially a source of memory consumption,
29         // but in reality unlikely to be a problem.
30         private readonly IList<Type[]> _appliedArguments = new List<Type[]>();
31
32         // Lazily initialised among potentially many exports
33         private ConstructorInfo _constructor;
34         private CompositeActivator _partActivator;
35
36         private static readonly IDictionary<string, object> s_noMetadata = new Dictionary<string, object>();
37         private static readonly MethodInfo s_activatorInvoke = typeof(CompositeActivator).GetTypeInfo().GetDeclaredMethod("Invoke");
38
39         private DiscoveredPart(
40             TypeInfo partType,
41             AttributedModelProvider attributeContext,
42             ActivationFeature[] activationFeatures,
43             Lazy<IDictionary<string, object>> partMetadata)
44         {
45             _partType = partType;
46             _attributeContext = attributeContext;
47             _activationFeatures = activationFeatures;
48             _partMetadata = partMetadata;
49         }
50
51         public DiscoveredPart(
52             TypeInfo partType,
53             AttributedModelProvider attributeContext,
54             ActivationFeature[] activationFeatures)
55         {
56             _partType = partType;
57             _attributeContext = attributeContext;
58             _activationFeatures = activationFeatures;
59             _partMetadata = new Lazy<IDictionary<string, object>>(() => GetPartMetadata(partType));
60         }
61
62         public TypeInfo PartType { get { return _partType; } }
63
64         public bool IsShared { get { return ContractHelpers.IsShared(_partMetadata.Value); } }
65
66         public void AddDiscoveredExport(DiscoveredExport export)
67         {
68             _exports.Add(export);
69             export.Part = this;
70         }
71
72         public CompositionDependency[] GetDependencies(DependencyAccessor definitionAccessor)
73         {
74             return GetPartActivatorDependencies(definitionAccessor)
75                 .Concat(_activationFeatures
76                     .SelectMany(feature => feature.GetDependencies(_partType, definitionAccessor)))
77                 .Where(a => a != null)
78                 .ToArray();
79         }
80
81         private IEnumerable<CompositionDependency> GetPartActivatorDependencies(DependencyAccessor definitionAccessor)
82         {
83             var partTypeAsType = _partType.AsType();
84
85             if (_constructor == null)
86             {
87                 foreach (var c in _partType.DeclaredConstructors.Where(ci => ci.IsPublic && !(ci.IsStatic)))
88                 {
89                     if (_attributeContext.GetDeclaredAttribute<ImportingConstructorAttribute>(partTypeAsType, c) != null)
90                     {
91                         if (_constructor != null)
92                         {
93                             string message = SR.Format(SR.DiscoveredPart_MultipleImportingConstructorsFound, _partType);
94                             throw new CompositionFailedException(message);
95                         }
96
97                         _constructor = c;
98                     }
99                 }
100
101                 if (_constructor == null && _partType.IsGenericType)
102                 {
103                     _constructor = GetConstructorInfoFromGenericType(_partType);
104                 }
105
106                 _constructor ??= _partType.DeclaredConstructors.FirstOrDefault(ci => ci.IsPublic && !(ci.IsStatic || ci.GetParameters().Any()));
107
108                 if (_constructor == null)
109                 {
110                     string message = SR.Format(SR.DiscoveredPart_NoImportingConstructorsFound, _partType);
111                     throw new CompositionFailedException(message);
112                 }
113             }
114
115             var cps = _constructor.GetParameters();
116
117             for (var i = 0; i < cps.Length; ++i)
118             {
119                 var pi = cps[i];
120                 var site = new ParameterImportSite(pi);
121
122                 var importInfo = ContractHelpers.GetImportInfo(pi.ParameterType, _attributeContext.GetDeclaredAttributes(partTypeAsType, pi), site);
123                 if (!importInfo.AllowDefault)
124                 {
125                     yield return definitionAccessor.ResolveRequiredDependency(site, importInfo.Contract, true);
126                 }
127                 else
128                 {
129                     CompositionDependency optional;
130                     if (definitionAccessor.TryResolveOptionalDependency(site, importInfo.Contract, true, out optional))
131                         yield return optional;
132                 }
133             }
134         }
135
136         private ConstructorInfo GetConstructorInfoFromGenericType(TypeInfo type)
137         {
138             Type genericPartType = type.GetGenericTypeDefinition();
139             TypeInfo genericPartTypeInfo = genericPartType.GetTypeInfo();
140             int constructorsCount = genericPartTypeInfo.DeclaredConstructors.Count();
141             ConstructorInfo constructor = null;
142
143             for (var index = 0; index < constructorsCount; index++)
144             {
145                 ConstructorInfo constructorInfo = genericPartTypeInfo.DeclaredConstructors.ElementAt(index);
146
147                 if (!constructorInfo.IsPublic || constructorInfo.IsStatic) continue;
148
149                 if (_attributeContext.GetDeclaredAttribute<ImportingConstructorAttribute>(genericPartType, constructorInfo) != null)
150                 {
151                     if (constructor != null)
152                     {
153                         string message = SR.Format(SR.DiscoveredPart_MultipleImportingConstructorsFound, type);
154                         throw new CompositionFailedException(message);
155                     }
156
157                     constructor = type.DeclaredConstructors.ElementAt(index);
158                 }
159             }
160
161             return constructor;
162         }
163
164         public CompositeActivator GetActivator(IEnumerable<CompositionDependency> dependencies)
165         {
166             if (_partActivator != null) return _partActivator;
167
168             var contextParam = Expression.Parameter(typeof(LifetimeContext), "cc");
169             var operationParm = Expression.Parameter(typeof(CompositionOperation), "op");
170
171             var cps = _constructor.GetParameters();
172             Expression[] paramActivatorCalls = new Expression[cps.Length];
173
174             var partActivatorDependencies = dependencies
175                 .Where(dep => dep.Site is ParameterImportSite)
176                 .ToDictionary(d => ((ParameterImportSite)d.Site).Parameter, ParameterInfoComparer.Instance);
177
178             for (var i = 0; i < cps.Length; ++i)
179             {
180                 var pi = cps[i];
181                 CompositionDependency dep;
182
183                 if (partActivatorDependencies.TryGetValue(pi, out dep))
184                 {
185                     var a = dep.Target.GetDescriptor().Activator;
186                     paramActivatorCalls[i] =
187                         Expression.Convert(Expression.Call(Expression.Constant(a), s_activatorInvoke, contextParam, operationParm), pi.ParameterType);
188                 }
189                 else
190                 {
191                     paramActivatorCalls[i] = Expression.Default(pi.ParameterType);
192                 }
193             }
194
195             Expression body = Expression.Convert(Expression.New(_constructor, paramActivatorCalls), typeof(object));
196
197             var activator = Expression
198                 .Lambda<CompositeActivator>(body, contextParam, operationParm)
199                 .Compile();
200
201             foreach (var activationFeature in _activationFeatures)
202                 activator = activationFeature.RewriteActivator(_partType, activator, _partMetadata.Value, dependencies);
203
204             _partActivator = activator;
205             return _partActivator;
206         }
207
208         public IDictionary<string, object> GetPartMetadata(TypeInfo partType)
209         {
210             var partMetadata = new Dictionary<string, object>();
211             foreach (var attr in _attributeContext.GetDeclaredAttributes(partType.AsType(), partType))
212             {
213                 if (attr is PartMetadataAttribute ma)
214                 {
215                     partMetadata.Add(ma.Name, ma.Value);
216                 }
217             }
218
219             return partMetadata.Count == 0 ? s_noMetadata : partMetadata;
220         }
221
222         public bool TryCloseGenericPart(Type[] typeArguments, out DiscoveredPart closed)
223         {
224             for (int index = 0; index < _partType.GenericTypeParameters.Length; index++)
225             {
226                 foreach (var genericParameterConstraints in _partType.GenericTypeParameters[index].GetTypeInfo().GetGenericParameterConstraints())
227                 {
228                     if (!genericParameterConstraints.GetTypeInfo().IsAssignableFrom(typeArguments[index].GetTypeInfo()))
229                     {
230                         closed = null;
231                         return false;
232                     }
233                 }
234             }
235
236             if (_appliedArguments.Any(args => Enumerable.SequenceEqual(args, typeArguments)))
237             {
238                 closed = null;
239                 return false;
240             }
241
242             _appliedArguments.Add(typeArguments);
243
244             var closedType = _partType.MakeGenericType(typeArguments).GetTypeInfo();
245
246             var result = new DiscoveredPart(closedType, _attributeContext, _activationFeatures, _partMetadata);
247
248             foreach (var export in _exports)
249             {
250                 var closedExport = export.CloseGenericExport(closedType, typeArguments);
251                 result.AddDiscoveredExport(closedExport);
252             }
253
254             closed = result;
255             return true;
256         }
257
258         public IEnumerable<DiscoveredExport> DiscoveredExports { get { return _exports; } }
259
260         // uses the fact that current usage only has comparisons
261         // between ParameterInfo objects from the same constructor reference,
262         // thus only the position needs to be compared.
263         // Equals checks the member reference equality in case usage changes.
264         private sealed class ParameterInfoComparer : IEqualityComparer<ParameterInfo>
265         {
266             public static readonly ParameterInfoComparer Instance = new ParameterInfoComparer();
267
268             public int GetHashCode(ParameterInfo obj)
269             {
270                 return HashHelpers.Combine(obj.Position.GetHashCode(), obj.Member.GetHashCode());
271             }
272
273             public bool Equals(ParameterInfo x, ParameterInfo y)
274             {
275                 if (ReferenceEquals(x, y))
276                 {
277                     return true;
278                 }
279
280                 if (x == null || y == null)
281                 {
282                     return false;
283                 }
284
285                 if (x.Position != y.Position)
286                 {
287                     return false;
288                 }
289
290                 if (x.Member != y.Member)
291                 {
292                     return false;
293                 }
294
295                 return true;
296             }
297         }
298     }
299 }