| | 1 | | // Copyright (c) ZeroC, Inc. |
| | 2 | |
|
| | 3 | | using System.Collections.Concurrent; |
| | 4 | | using System.Collections.Immutable; |
| | 5 | | using System.Globalization; |
| | 6 | | using System.Reflection; |
| | 7 | |
|
| | 8 | | namespace ZeroC.Slice.Internal; |
| | 9 | |
|
| | 10 | | /// <summary>The default implementation of <see cref="IActivator" />, which uses a dictionary.</summary> |
| | 11 | | internal class Activator : IActivator |
| | 12 | | { |
| | 13 | | internal static Activator Empty { get; } = new Activator(ImmutableDictionary<string, Type>.Empty); |
| | 14 | |
|
| | 15 | | private readonly IReadOnlyDictionary<string, Type> _dict; |
| | 16 | |
|
| | 17 | | public object? CreateInstance(string typeId) => |
| | 18 | | _dict.TryGetValue(typeId, out Type? type) ? System.Activator.CreateInstance(type) : null; |
| | 19 | |
|
| | 20 | | /// <summary>Merge activators into a single activator; duplicate entries are ignored.</summary> |
| | 21 | | internal static Activator Merge(IEnumerable<Activator> activators) |
| | 22 | | { |
| | 23 | | if (activators.Count() == 1) |
| | 24 | | { |
| | 25 | | return activators.First(); |
| | 26 | | } |
| | 27 | | else |
| | 28 | | { |
| | 29 | | var dict = new Dictionary<string, Type>(); |
| | 30 | |
|
| | 31 | | foreach (Activator activator in activators) |
| | 32 | | { |
| | 33 | | foreach ((string typeId, Type factory) in activator._dict) |
| | 34 | | { |
| | 35 | | dict[typeId] = factory; |
| | 36 | | } |
| | 37 | | } |
| | 38 | | return dict.Count == 0 ? Empty : new Activator(dict); |
| | 39 | | } |
| | 40 | | } |
| | 41 | |
|
| | 42 | | internal Activator(IReadOnlyDictionary<string, Type> dict) => _dict = dict; |
| | 43 | | } |
| | 44 | |
|
| | 45 | | /// <summary>Creates activators from assemblies by processing types in those assemblies.</summary> |
| | 46 | | internal class ActivatorFactory |
| | 47 | | { |
| 118 | 48 | | internal static ActivatorFactory Instance { get; } = new ActivatorFactory(); |
| | 49 | |
|
| 8 | 50 | | private readonly ConcurrentDictionary<Assembly, Activator> _cache = new(); |
| | 51 | |
|
| | 52 | | internal Activator Get(Assembly assembly) |
| 380 | 53 | | { |
| 380 | 54 | | if (_cache.TryGetValue(assembly, out Activator? activator)) |
| 116 | 55 | | { |
| 116 | 56 | | return activator; |
| | 57 | | } |
| 264 | 58 | | else if (assembly.GetCustomAttributes<SliceAttribute>().Any()) |
| 12 | 59 | | { |
| 12 | 60 | | return _cache.GetOrAdd( |
| 12 | 61 | | assembly, |
| 12 | 62 | | assembly => |
| 12 | 63 | | { |
| 12 | 64 | | var dict = new Dictionary<string, Type>(); |
| 12 | 65 | |
|
| 4550 | 66 | | foreach (Type type in assembly.GetTypes()) |
| 2257 | 67 | | { |
| 12 | 68 | | // We're only interested in generated Slice classes and exceptions. |
| 2257 | 69 | | if (type.IsClass && type.GetSliceTypeId() is string typeId) |
| 63 | 70 | | { |
| 63 | 71 | | dict.Add(typeId, type); |
| 12 | 72 | |
|
| 63 | 73 | | if (type.GetCompactSliceTypeId() is int compactTypeId) |
| 10 | 74 | | { |
| 10 | 75 | | dict.Add(compactTypeId.ToString(CultureInfo.InvariantCulture), type); |
| 10 | 76 | | } |
| 63 | 77 | | } |
| 2257 | 78 | | } |
| 12 | 79 | |
|
| 12 | 80 | | // Merge with the activators of the referenced assemblies (recursive call) |
| 12 | 81 | | return Activator.Merge( |
| 12 | 82 | | assembly.GetReferencedAssemblies().Select( |
| 262 | 83 | | assemblyName => Get(AppDomain.CurrentDomain.Load(assemblyName))).Append( |
| 12 | 84 | | new Activator(dict))); |
| 24 | 85 | | }); |
| | 86 | | } |
| | 87 | | else |
| 252 | 88 | | { |
| | 89 | | // We don't cache an assembly with no Slice attribute, and don't load/process its referenced assemblies. |
| 252 | 90 | | return Activator.Empty; |
| | 91 | | } |
| 380 | 92 | | } |
| | 93 | |
|
| 8 | 94 | | private ActivatorFactory() |
| 8 | 95 | | { |
| | 96 | | // ensures it's a singleton. |
| 8 | 97 | | } |
| | 98 | | } |