internal static StObjMapInfo?Create(IActivityMonitor m, Assembly a, CustomAttributeData attr)
 {
     try
     {
         object?v = attr.AttributeType.GetField("V", BindingFlags.Public | BindingFlags.Static)?.GetValue(null);
         if (v == null)
         {
             m.Error($"Unable to retrieve the CK.StObj.Signature assembly attribute from '{a.FullName}'.");
         }
         else
         {
             (SHA1Value, IReadOnlyList <string>)s = ((SHA1Value, IReadOnlyList <string>))v;
             Type?t = a.GetType(StObjContextRoot.RootContextTypeFullName, false, false);
             if (t == null)
             {
                 m.Error($"Unable to retrieve the generated {StObjContextRoot.RootContextTypeFullName} type from '{a.FullName}'.");
             }
             else
             {
                 var r = new StObjMapInfo(s.Item1, s.Item2, t);
                 m.Info($"Found StObjMap: {r}.");
                 return(r);
             }
         }
     }
     catch (Exception ex)
     {
         m.Error("Unable to read StObjMap information.", ex);
     }
     return(null);
 }
        static StObjMapInfo?LockedGetMapInfo(Assembly a, [AllowNull] ref IActivityMonitor monitor)
        {
            if (_alreadyHandled.TryGetValue(a, out var info))
            {
                return(info);
            }
            LockedEnsureMonitor(ref monitor);
            var attr = a.GetCustomAttributesData().FirstOrDefault(m => m.AttributeType.Name == "SignatureAttribute" && m.AttributeType.Namespace == "CK.StObj");

            if (attr != null)
            {
                using (monitor.OpenInfo($"Analyzing '{a.FullName}' assembly."))
                {
                    info = StObjMapInfo.Create(monitor, a, attr);
                    if (info != null)
                    {
                        var sha1S = info.GeneratedSignature.ToString();
                        if (_alreadyHandled.TryGetValue(sha1S, out var exists))
                        {
                            Debug.Assert(exists != null);
                            monitor.Info($"StObjMap found with the same signature as an already existing one. Keeping the previous one.");
                            info = exists;
                        }
                        else
                        {
                            _alreadyHandled.Add(sha1S, info);
                            _availableMaps.Add(info);
                        }
                    }
                }
                _alreadyHandled.Add(a, info);
            }
            return(info);
        }
 /// <summary>
 /// Gets the <see cref="IStObjMap"/> if no error prevents its instantiation from
 /// the <see cref="StObjMapInfo"/>.
 /// This never throws: errors are logged (a new monitor is automatically managed when <paramref name="monitor"/> is null),
 /// and null is returned.
 /// This method like all the methods that manipulates StObjMapInfo are thread/concurrency safe.
 /// </summary>
 /// <param name="info">The info.</param>
 /// <param name="monitor">Optional monitor.</param>
 /// <returns>The loaded map or null on error.</returns>
 public static IStObjMap?GetStObjMap(StObjMapInfo info, IActivityMonitor?monitor = null)
 {
     Throw.CheckNotNullArgument(info);
     if (info.StObjMap != null || info.LoadError != null)
     {
         return(info.StObjMap);
     }
     lock ( _alreadyHandled )
     {
         return(LockedGetStObjMapFromInfo(info, ref monitor));
     }
 }
 static IStObjMap?LockedGetStObjMapFromInfo(StObjMapInfo info, [NotNullIfNotNull("monitor")] ref IActivityMonitor?monitor)
 {
     if (info.StObjMap != null || info.LoadError != null)
     {
         return(info.StObjMap);
     }
     LockedEnsureMonitor(ref monitor);
     using (monitor.OpenInfo($"Instantiating StObjMap from {info}."))
     {
         try
         {
             return(info.StObjMap = (IStObjMap?)Activator.CreateInstance(info.StObjMapType, new object[] { monitor }));
         }
         catch (Exception ex)
         {
             monitor.Error(ex);
             info.LoadError = ex.Message;
             return(null);
         }
     }
 }