| File: System\Composition\TypedParts\Discovery\DiscoveredPart.cs | Web Access |
| Project: src\runtime\src\libraries\System.Composition.TypedParts\src\System.Composition.TypedParts.csproj (System.Composition.TypedParts) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Composition.Convention; using System.Composition.Debugging; using System.Composition.Hosting; using System.Composition.Hosting.Core; using System.Composition.TypedParts.ActivationFeatures; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Numerics.Hashing; using System.Reflection; namespace System.Composition.TypedParts.Discovery { [DebuggerDisplay("{PartType.Name}")] [DebuggerTypeProxy(typeof(DiscoveredPartDebuggerProxy))] internal sealed class DiscoveredPart { private readonly TypeInfo _partType; private readonly AttributedModelProvider _attributeContext; private readonly List<DiscoveredExport> _exports = new List<DiscoveredExport>(); private readonly ActivationFeature[] _activationFeatures; private readonly Lazy<IDictionary<string, object>> _partMetadata; // This is unbounded so potentially a source of memory consumption, // but in reality unlikely to be a problem. private readonly List<Type[]> _appliedArguments = new List<Type[]>(); // Lazily initialised among potentially many exports private ConstructorInfo _constructor; private CompositeActivator _partActivator; private static readonly IDictionary<string, object> s_noMetadata = new Dictionary<string, object>(); private static readonly MethodInfo s_activatorInvoke = typeof(CompositeActivator).GetTypeInfo().GetDeclaredMethod("Invoke"); private DiscoveredPart( TypeInfo partType, AttributedModelProvider attributeContext, ActivationFeature[] activationFeatures, Lazy<IDictionary<string, object>> partMetadata) { _partType = partType; _attributeContext = attributeContext; _activationFeatures = activationFeatures; _partMetadata = partMetadata; } public DiscoveredPart( TypeInfo partType, AttributedModelProvider attributeContext, ActivationFeature[] activationFeatures) { _partType = partType; _attributeContext = attributeContext; _activationFeatures = activationFeatures; _partMetadata = new Lazy<IDictionary<string, object>>(() => GetPartMetadata(partType)); } public TypeInfo PartType { get { return _partType; } } public bool IsShared { get { return ContractHelpers.IsShared(_partMetadata.Value); } } public void AddDiscoveredExport(DiscoveredExport export) { _exports.Add(export); export.Part = this; } public CompositionDependency[] GetDependencies(DependencyAccessor definitionAccessor) { return GetPartActivatorDependencies(definitionAccessor) .Concat(_activationFeatures .SelectMany(feature => feature.GetDependencies(_partType, definitionAccessor))) .Where(a => a != null) .ToArray(); } private IEnumerable<CompositionDependency> GetPartActivatorDependencies(DependencyAccessor definitionAccessor) { var partTypeAsType = _partType.AsType(); if (_constructor == null) { foreach (var c in _partType.DeclaredConstructors.Where(ci => ci.IsPublic && !(ci.IsStatic))) { if (_attributeContext.GetDeclaredAttribute<ImportingConstructorAttribute>(partTypeAsType, c) != null) { if (_constructor != null) { string message = SR.Format(SR.DiscoveredPart_MultipleImportingConstructorsFound, _partType); throw new CompositionFailedException(message); } _constructor = c; } } if (_constructor == null && _partType.IsGenericType) { _constructor = GetConstructorInfoFromGenericType(_partType); } _constructor ??= _partType.DeclaredConstructors.FirstOrDefault(ci => ci.IsPublic && !(ci.IsStatic || ci.GetParameters().Length != 0)); if (_constructor == null) { string message = SR.Format(SR.DiscoveredPart_NoImportingConstructorsFound, _partType); throw new CompositionFailedException(message); } } var cps = _constructor.GetParameters(); for (var i = 0; i < cps.Length; ++i) { var pi = cps[i]; var site = new ParameterImportSite(pi); var importInfo = ContractHelpers.GetImportInfo(pi.ParameterType, _attributeContext.GetDeclaredAttributes(partTypeAsType, pi), site); if (!importInfo.AllowDefault) { yield return definitionAccessor.ResolveRequiredDependency(site, importInfo.Contract, true); } else { CompositionDependency optional; if (definitionAccessor.TryResolveOptionalDependency(site, importInfo.Contract, true, out optional)) yield return optional; } } } private ConstructorInfo GetConstructorInfoFromGenericType(TypeInfo type) { Type genericPartType = type.GetGenericTypeDefinition(); TypeInfo genericPartTypeInfo = genericPartType.GetTypeInfo(); int constructorsCount = genericPartTypeInfo.DeclaredConstructors.Count(); ConstructorInfo constructor = null; for (var index = 0; index < constructorsCount; index++) { ConstructorInfo constructorInfo = genericPartTypeInfo.DeclaredConstructors.ElementAt(index); if (!constructorInfo.IsPublic || constructorInfo.IsStatic) continue; if (_attributeContext.GetDeclaredAttribute<ImportingConstructorAttribute>(genericPartType, constructorInfo) != null) { if (constructor != null) { string message = SR.Format(SR.DiscoveredPart_MultipleImportingConstructorsFound, type); throw new CompositionFailedException(message); } constructor = type.DeclaredConstructors.ElementAt(index); } } return constructor; } public CompositeActivator GetActivator(IEnumerable<CompositionDependency> dependencies) { if (_partActivator != null) return _partActivator; var contextParam = Expression.Parameter(typeof(LifetimeContext), "cc"); var operationParm = Expression.Parameter(typeof(CompositionOperation), "op"); var cps = _constructor.GetParameters(); Expression[] paramActivatorCalls = new Expression[cps.Length]; var partActivatorDependencies = dependencies .Where(dep => dep.Site is ParameterImportSite) .ToDictionary(d => ((ParameterImportSite)d.Site).Parameter, ParameterInfoComparer.Instance); for (var i = 0; i < cps.Length; ++i) { var pi = cps[i]; CompositionDependency dep; if (partActivatorDependencies.TryGetValue(pi, out dep)) { var a = dep.Target.GetDescriptor().Activator; paramActivatorCalls[i] = Expression.Convert(Expression.Call(Expression.Constant(a), s_activatorInvoke, contextParam, operationParm), pi.ParameterType); } else { paramActivatorCalls[i] = Expression.Default(pi.ParameterType); } } Expression body = Expression.Convert(Expression.New(_constructor, paramActivatorCalls), typeof(object)); var activator = Expression .Lambda<CompositeActivator>(body, contextParam, operationParm) .Compile(); foreach (var activationFeature in _activationFeatures) activator = activationFeature.RewriteActivator(_partType, activator, _partMetadata.Value, dependencies); _partActivator = activator; return _partActivator; } public IDictionary<string, object> GetPartMetadata(TypeInfo partType) { var partMetadata = new Dictionary<string, object>(); foreach (var attr in _attributeContext.GetDeclaredAttributes(partType.AsType(), partType)) { if (attr is PartMetadataAttribute ma) { partMetadata.Add(ma.Name, ma.Value); } } return partMetadata.Count == 0 ? s_noMetadata : partMetadata; } public bool TryCloseGenericPart(Type[] typeArguments, out DiscoveredPart closed) { for (int index = 0; index < _partType.GenericTypeParameters.Length; index++) { foreach (var genericParameterConstraints in _partType.GenericTypeParameters[index].GetTypeInfo().GetGenericParameterConstraints()) { if (!genericParameterConstraints.GetTypeInfo().IsAssignableFrom(typeArguments[index].GetTypeInfo())) { closed = null; return false; } } } if (_appliedArguments.Any(args => Enumerable.SequenceEqual(args, typeArguments))) { closed = null; return false; } _appliedArguments.Add(typeArguments); var closedType = _partType.MakeGenericType(typeArguments).GetTypeInfo(); var result = new DiscoveredPart(closedType, _attributeContext, _activationFeatures, _partMetadata); foreach (var export in _exports) { var closedExport = export.CloseGenericExport(closedType, typeArguments); result.AddDiscoveredExport(closedExport); } closed = result; return true; } public IEnumerable<DiscoveredExport> DiscoveredExports { get { return _exports; } } // uses the fact that current usage only has comparisons // between ParameterInfo objects from the same constructor reference, // thus only the position needs to be compared. // Equals checks the member reference equality in case usage changes. private sealed class ParameterInfoComparer : IEqualityComparer<ParameterInfo> { public static readonly ParameterInfoComparer Instance = new ParameterInfoComparer(); public int GetHashCode(ParameterInfo obj) { return HashHelpers.Combine(obj.Position.GetHashCode(), obj.Member.GetHashCode()); } public bool Equals(ParameterInfo x, ParameterInfo y) { if (ReferenceEquals(x, y)) { return true; } if (x == null || y == null) { return false; } if (x.Position != y.Position) { return false; } if (x.Member != y.Member) { return false; } return true; } } } }