From 15e6f8fa0edeac001a64148932e49283b1820eb5 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Sun, 18 Apr 2021 14:53:39 -0400 Subject: [PATCH 1/2] Add metadata update handler to System.ComponentModel.TypeConverter --- ...System.ComponentModel.TypeConverter.csproj | 2 + .../ReflectTypeDescriptionProvider.cs | 9 +++++ .../ReflectionCachesUpdateHandler.cs | 39 +++++++++++++++++++ .../ReflectionCachesUpdateHandlerTests.cs | 32 +++++++++++++++ ....ComponentModel.TypeConverter.Tests.csproj | 1 + 5 files changed, 83 insertions(+) create mode 100644 src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectionCachesUpdateHandler.cs create mode 100644 src/libraries/System.ComponentModel.TypeConverter/tests/ReflectionCachesUpdateHandlerTests.cs diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj b/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj index a93c8e4aadc9d..5312108604e3c 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj @@ -70,6 +70,7 @@ + @@ -253,6 +254,7 @@ + diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs index de3b12258cca6..8962f6c7b5d13 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs @@ -204,6 +204,15 @@ private static EnumConverter CreateEnumConverter(Type type) private static Hashtable ExtendedPropertyCache => LazyInitializer.EnsureInitialized(ref s_extendedPropertyCache, () => new Hashtable()); + /// Clear the global caches this maintains on top of reflection. + internal static void ClearReflectionCaches() + { + Volatile.Write(ref s_propertyCache, null); + Volatile.Write(ref s_eventCache, null); + Volatile.Write(ref s_attributeCache, null); + Volatile.Write(ref s_extendedPropertyCache, null); + } + /// /// Adds an editor table for the given editor base type. /// Typically, editors are specified as metadata on an object. If no metadata for a diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectionCachesUpdateHandler.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectionCachesUpdateHandler.cs new file mode 100644 index 0000000000000..fe2a5306553a9 --- /dev/null +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectionCachesUpdateHandler.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable +using System.ComponentModel; +using System.Reflection; +using System.Reflection.Metadata; + +[assembly: MetadataUpdateHandler(typeof(ReflectionCachesUpdateHandler))] + +namespace System.ComponentModel +{ + internal static class ReflectionCachesUpdateHandler + { + public static void BeforeUpdate(Type[]? types) + { + // ReflectTypeDescriptionProvider maintains global caches on top of reflection. + // Clear those. + ReflectTypeDescriptionProvider.ClearReflectionCaches(); + + // Each type descriptor may also cache reflection-based state that it gathered + // from ReflectTypeDescriptionProvider. Clear those as well. + if (types is not null) + { + foreach (Type type in types) + { + TypeDescriptor.Refresh(type); + } + } + else + { + foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + TypeDescriptor.Refresh(assembly); + } + } + } + } +} diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/ReflectionCachesUpdateHandlerTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/ReflectionCachesUpdateHandlerTests.cs new file mode 100644 index 0000000000000..829bbd5b1edce --- /dev/null +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/ReflectionCachesUpdateHandlerTests.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection; +using Xunit; + +namespace System.ComponentModel.Tests +{ + [SimpleUpdateTest] + public class ReflectionCachesUpdateHandlerTests + { + [Fact] + public void ReflectionCachesUpdateHandler_CachesCleared() + { + AttributeCollection ac1 = TypeDescriptor.GetAttributes(typeof(ReflectionCachesUpdateHandlerTests)); + AttributeCollection ac2 = TypeDescriptor.GetAttributes(typeof(ReflectionCachesUpdateHandlerTests)); + Assert.Equal(ac1.Count, ac2.Count); + Assert.Equal(1, ac1.Count); + Assert.Same(ac1[0], ac2[0]); + + MethodInfo beforeUpdate = typeof(TypeDescriptionProvider).Assembly.GetType("System.ComponentModel.ReflectionCachesUpdateHandler", throwOnError: true).GetMethod("BeforeUpdate"); + Assert.NotNull(beforeUpdate); + beforeUpdate.Invoke(null, new object[] { null }); + + AttributeCollection ac3 = TypeDescriptor.GetAttributes(typeof(ReflectionCachesUpdateHandlerTests)); + Assert.NotSame(ac1[0], ac3[0]); + } + } + + [AttributeUsage(AttributeTargets.All)] + internal sealed class SimpleUpdateTestAttribute : Attribute { } +} diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj b/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj index b8a634b041a8f..4f96c81aea140 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/System.ComponentModel.TypeConverter.Tests.csproj @@ -104,6 +104,7 @@ + From 6cab30cb1314e8dd32b48436659534fbd1e0d380 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 19 Apr 2021 21:04:30 -0400 Subject: [PATCH 2/2] Update src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs --- .../ComponentModel/ReflectTypeDescriptionProvider.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs index 8962f6c7b5d13..24361c55c829e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs @@ -207,10 +207,10 @@ private static EnumConverter CreateEnumConverter(Type type) /// Clear the global caches this maintains on top of reflection. internal static void ClearReflectionCaches() { - Volatile.Write(ref s_propertyCache, null); - Volatile.Write(ref s_eventCache, null); - Volatile.Write(ref s_attributeCache, null); - Volatile.Write(ref s_extendedPropertyCache, null); + s_propertyCache = null; + s_eventCache = null; + s_attributeCache = null; + s_extendedPropertyCache = null; } ///