// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using Microsoft.ClearScript.Util; namespace Microsoft.ClearScript { internal sealed class CustomAttributeCache { private readonly ConditionalWeakTable table = new(); public T[] GetOrLoad(CustomAttributeLoader loader, ICustomAttributeProvider resource, bool inherit) where T : Attribute { lock (table) { return GetOrLoad(loader, table.GetOrCreateValue(resource), resource, inherit); } } private T[] GetOrLoad(CustomAttributeLoader loader, Entry entry, ICustomAttributeProvider resource, bool inherit) where T : Attribute { if (entry.TryGet(out var attrs)) { return attrs; } attrs = Load(GetIsBypass(entry, resource) ? CustomAttributeLoader.Default : loader, resource, inherit); entry.Add(attrs); return attrs; } private static T[] Load(CustomAttributeLoader loader, ICustomAttributeProvider resource, bool inherit) where T : Attribute { return loader.LoadCustomAttributes(resource, inherit) ?? ArrayHelpers.GetEmptyArray(); } private bool GetIsBypass(ICustomAttributeProvider resource) { // ReSharper disable once InconsistentlySynchronizedField return GetIsBypass(table.GetOrCreateValue(resource), resource); } private bool GetIsBypass(Entry entry, ICustomAttributeProvider resource) { if (!entry.IsBypass.HasValue) { entry.IsBypass = GetIsBypassInternal(resource); } return entry.IsBypass.Value; } private bool GetIsBypassInternal(ICustomAttributeProvider resource) { if (Load(CustomAttributeLoader.Default, resource, false).Length > 0) { return true; } var parent = GetParent(resource); if (parent is not null) { return GetIsBypass(parent); } return false; } private static ICustomAttributeProvider GetParent(ICustomAttributeProvider resource) { if (resource is ParameterInfo parameter) { return parameter.Member; } if (resource is Type type) { return (type.DeclaringType as ICustomAttributeProvider) ?? type.Module; } if (resource is MemberInfo member) { return member.DeclaringType; } if (resource is Module module) { return module.Assembly; } return null; } #region Nested type: Entry // ReSharper disable ClassNeverInstantiated.Local private sealed class Entry { private readonly Dictionary map = new(); public bool? IsBypass { get; set; } public void Add(T[] attrs) { map.Add(typeof(T), attrs); } public bool TryGet(out T[] attrs) { if (map.TryGetValue(typeof(T), out var attrsObject)) { attrs = attrsObject as T[]; return true; } attrs = null; return false; } } // ReSharper restore ClassNeverInstantiated.Local #endregion } [AttributeUsage(AttributeTargets.All, Inherited = false)] internal sealed class BypassCustomAttributeLoaderAttribute : Attribute { } }