1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
4 using System.Collections.Generic;
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;
16 namespace System.Composition.TypedParts.Discovery
18 [DebuggerDisplay("{PartType.Name}")]
19 [DebuggerTypeProxy(typeof(DiscoveredPartDebuggerProxy))]
20 internal sealed class DiscoveredPart
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;
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[]>();
32 // Lazily initialised among potentially many exports
33 private ConstructorInfo _constructor;
34 private CompositeActivator _partActivator;
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");
39 private DiscoveredPart(
41 AttributedModelProvider attributeContext,
42 ActivationFeature[] activationFeatures,
43 Lazy<IDictionary<string, object>> partMetadata)
46 _attributeContext = attributeContext;
47 _activationFeatures = activationFeatures;
48 _partMetadata = partMetadata;
51 public DiscoveredPart(
53 AttributedModelProvider attributeContext,
54 ActivationFeature[] activationFeatures)
57 _attributeContext = attributeContext;
58 _activationFeatures = activationFeatures;
59 _partMetadata = new Lazy<IDictionary<string, object>>(() => GetPartMetadata(partType));
62 public TypeInfo PartType { get { return _partType; } }
64 public bool IsShared { get { return ContractHelpers.IsShared(_partMetadata.Value); } }
66 public void AddDiscoveredExport(DiscoveredExport export)
72 public CompositionDependency[] GetDependencies(DependencyAccessor definitionAccessor)
74 return GetPartActivatorDependencies(definitionAccessor)
75 .Concat(_activationFeatures
76 .SelectMany(feature => feature.GetDependencies(_partType, definitionAccessor)))
77 .Where(a => a != null)
81 private IEnumerable<CompositionDependency> GetPartActivatorDependencies(DependencyAccessor definitionAccessor)
83 var partTypeAsType = _partType.AsType();
85 if (_constructor == null)
87 foreach (var c in _partType.DeclaredConstructors.Where(ci => ci.IsPublic && !(ci.IsStatic)))
89 if (_attributeContext.GetDeclaredAttribute<ImportingConstructorAttribute>(partTypeAsType, c) != null)
91 if (_constructor != null)
93 string message = SR.Format(SR.DiscoveredPart_MultipleImportingConstructorsFound, _partType);
94 throw new CompositionFailedException(message);
101 if (_constructor == null && _partType.IsGenericType)
103 _constructor = GetConstructorInfoFromGenericType(_partType);
106 _constructor ??= _partType.DeclaredConstructors.FirstOrDefault(ci => ci.IsPublic && !(ci.IsStatic || ci.GetParameters().Any()));
108 if (_constructor == null)
110 string message = SR.Format(SR.DiscoveredPart_NoImportingConstructorsFound, _partType);
111 throw new CompositionFailedException(message);
115 var cps = _constructor.GetParameters();
117 for (var i = 0; i < cps.Length; ++i)
120 var site = new ParameterImportSite(pi);
122 var importInfo = ContractHelpers.GetImportInfo(pi.ParameterType, _attributeContext.GetDeclaredAttributes(partTypeAsType, pi), site);
123 if (!importInfo.AllowDefault)
125 yield return definitionAccessor.ResolveRequiredDependency(site, importInfo.Contract, true);
129 CompositionDependency optional;
130 if (definitionAccessor.TryResolveOptionalDependency(site, importInfo.Contract, true, out optional))
131 yield return optional;
136 private ConstructorInfo GetConstructorInfoFromGenericType(TypeInfo type)
138 Type genericPartType = type.GetGenericTypeDefinition();
139 TypeInfo genericPartTypeInfo = genericPartType.GetTypeInfo();
140 int constructorsCount = genericPartTypeInfo.DeclaredConstructors.Count();
141 ConstructorInfo constructor = null;
143 for (var index = 0; index < constructorsCount; index++)
145 ConstructorInfo constructorInfo = genericPartTypeInfo.DeclaredConstructors.ElementAt(index);
147 if (!constructorInfo.IsPublic || constructorInfo.IsStatic) continue;
149 if (_attributeContext.GetDeclaredAttribute<ImportingConstructorAttribute>(genericPartType, constructorInfo) != null)
151 if (constructor != null)
153 string message = SR.Format(SR.DiscoveredPart_MultipleImportingConstructorsFound, type);
154 throw new CompositionFailedException(message);
157 constructor = type.DeclaredConstructors.ElementAt(index);
164 public CompositeActivator GetActivator(IEnumerable<CompositionDependency> dependencies)
166 if (_partActivator != null) return _partActivator;
168 var contextParam = Expression.Parameter(typeof(LifetimeContext), "cc");
169 var operationParm = Expression.Parameter(typeof(CompositionOperation), "op");
171 var cps = _constructor.GetParameters();
172 Expression[] paramActivatorCalls = new Expression[cps.Length];
174 var partActivatorDependencies = dependencies
175 .Where(dep => dep.Site is ParameterImportSite)
176 .ToDictionary(d => ((ParameterImportSite)d.Site).Parameter, ParameterInfoComparer.Instance);
178 for (var i = 0; i < cps.Length; ++i)
181 CompositionDependency dep;
183 if (partActivatorDependencies.TryGetValue(pi, out dep))
185 var a = dep.Target.GetDescriptor().Activator;
186 paramActivatorCalls[i] =
187 Expression.Convert(Expression.Call(Expression.Constant(a), s_activatorInvoke, contextParam, operationParm), pi.ParameterType);
191 paramActivatorCalls[i] = Expression.Default(pi.ParameterType);
195 Expression body = Expression.Convert(Expression.New(_constructor, paramActivatorCalls), typeof(object));
197 var activator = Expression
198 .Lambda<CompositeActivator>(body, contextParam, operationParm)
201 foreach (var activationFeature in _activationFeatures)
202 activator = activationFeature.RewriteActivator(_partType, activator, _partMetadata.Value, dependencies);
204 _partActivator = activator;
205 return _partActivator;
208 public IDictionary<string, object> GetPartMetadata(TypeInfo partType)
210 var partMetadata = new Dictionary<string, object>();
211 foreach (var attr in _attributeContext.GetDeclaredAttributes(partType.AsType(), partType))
213 if (attr is PartMetadataAttribute ma)
215 partMetadata.Add(ma.Name, ma.Value);
219 return partMetadata.Count == 0 ? s_noMetadata : partMetadata;
222 public bool TryCloseGenericPart(Type[] typeArguments, out DiscoveredPart closed)
224 for (int index = 0; index < _partType.GenericTypeParameters.Length; index++)
226 foreach (var genericParameterConstraints in _partType.GenericTypeParameters[index].GetTypeInfo().GetGenericParameterConstraints())
228 if (!genericParameterConstraints.GetTypeInfo().IsAssignableFrom(typeArguments[index].GetTypeInfo()))
236 if (_appliedArguments.Any(args => Enumerable.SequenceEqual(args, typeArguments)))
242 _appliedArguments.Add(typeArguments);
244 var closedType = _partType.MakeGenericType(typeArguments).GetTypeInfo();
246 var result = new DiscoveredPart(closedType, _attributeContext, _activationFeatures, _partMetadata);
248 foreach (var export in _exports)
250 var closedExport = export.CloseGenericExport(closedType, typeArguments);
251 result.AddDiscoveredExport(closedExport);
258 public IEnumerable<DiscoveredExport> DiscoveredExports { get { return _exports; } }
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>
266 public static readonly ParameterInfoComparer Instance = new ParameterInfoComparer();
268 public int GetHashCode(ParameterInfo obj)
270 return HashHelpers.Combine(obj.Position.GetHashCode(), obj.Member.GetHashCode());
273 public bool Equals(ParameterInfo x, ParameterInfo y)
275 if (ReferenceEquals(x, y))
280 if (x == null || y == null)
285 if (x.Position != y.Position)
290 if (x.Member != y.Member)