Skip to content

Commit

Permalink
Update MetadataUpdateHandlers (#52409)
Browse files Browse the repository at this point in the history
  • Loading branch information
pranavkm committed May 7, 2021
1 parent 55b613c commit 594e87a
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 22 deletions.
Expand Up @@ -14,7 +14,7 @@ internal static class RuntimeTypeMetadataUpdateHandler
/// <summary>Clear type caches in response to an update notification.</summary>
/// <param name="types">The specific types to be cleared, or null to clear everything.</param>
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Clearing the caches on a Type isn't affected if a Type is trimmed, or has any of its members trimmed.")]
public static void BeforeUpdate(Type[]? types)
public static void ClearCache(Type[]? types)
{
if (RequiresClearingAllTypes(types))
{
Expand Down
Expand Up @@ -12,7 +12,7 @@ namespace System.ComponentModel
{
internal static class ReflectionCachesUpdateHandler
{
public static void BeforeUpdate(Type[]? types)
public static void ClearCache(Type[]? types)
{
// ReflectTypeDescriptionProvider maintains global caches on top of reflection.
// Clear those.
Expand Down
Expand Up @@ -18,9 +18,9 @@ public void ReflectionCachesUpdateHandler_CachesCleared()
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 });
MethodInfo clearCache = typeof(TypeDescriptionProvider).Assembly.GetType("System.ComponentModel.ReflectionCachesUpdateHandler", throwOnError: true).GetMethod("ClearCache");
Assert.NotNull(clearCache);
clearCache.Invoke(null, new object[] { null });

AttributeCollection ac3 = TypeDescriptor.GetAttributes(typeof(ReflectionCachesUpdateHandlerTests));
Assert.NotSame(ac1[0], ac3[0]);
Expand Down
Expand Up @@ -5,7 +5,20 @@

namespace System.Reflection.Metadata
{
/// <summary>Specifies a type that should receive notifications of metadata updates.</summary>
/// <summary>
/// Specifies a type that should receive notifications of metadata updates.
/// <para>
/// The <see cref="Type" /> specified by this attribute must have at least one static method with the following signature:
/// <c>static void ClearCache(Type[]? updatedTypes)</c>
/// <c>static void UpdateApplication(Type[]? updatedTypes)</c>
/// </para>
/// <para>
/// Once a metadata update is applied, <c>ClearCache</c> is invoked for every handler that specifies one. This gives update handlers
/// an opportunity to clear any caches that are inferred based from the application's metadata. This is followed by invoking the <c>UpdateHandler</c>
/// method is invoked letting applications update their contents, trigger a UI re-render etc. When specified, the <c>updatedTypes</c>
/// parameter indicates the sequence of types that were affected by the metadata update.
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class MetadataUpdateHandlerAttribute : Attribute
{
Expand Down
Expand Up @@ -23,13 +23,13 @@ public void GetMethod_MultipleCalls_SameObjects()

[ActiveIssue("https://github.com/dotnet/runtime/issues/50978", TestRuntimes.Mono)]
[Fact]
public void InvokeBeforeUpdate_NoExceptions()
public void InvokeClearCache_NoExceptions()
{
Action<Type[]> beforeUpdate = GetBeforeUpdateMethod();
beforeUpdate(null);
beforeUpdate(new Type[0]);
beforeUpdate(new Type[] { typeof(ReflectionCacheTests) });
beforeUpdate(new Type[] { typeof(string), typeof(int) });
Action<Type[]> clearCache = GetClearCacheMethod();
clearCache(null);
clearCache(new Type[0]);
clearCache(new Type[] { typeof(ReflectionCacheTests) });
clearCache(new Type[] { typeof(string), typeof(int) });
}

[ActiveIssue("https://github.com/dotnet/runtime/issues/50978", TestRuntimes.Mono)]
Expand All @@ -38,13 +38,13 @@ public void InvokeBeforeUpdate_NoExceptions()
[InlineData(true)]
public void GetMethod_MultipleCalls_ClearCache_DifferentObjects(bool justSpecificType)
{
Action<Type[]> beforeUpdate = GetBeforeUpdateMethod();
Action<Type[]> clearCache = GetClearCacheMethod();

MethodInfo mi1 = typeof(ReflectionCacheTests).GetMethod(nameof(GetMethod_MultipleCalls_ClearCache_DifferentObjects));
Assert.NotNull(mi1);
Assert.Equal(nameof(GetMethod_MultipleCalls_ClearCache_DifferentObjects), mi1.Name);

beforeUpdate(justSpecificType ? new[] { typeof(ReflectionCacheTests) } : null);
clearCache(justSpecificType ? new[] { typeof(ReflectionCacheTests) } : null);

MethodInfo mi2 = typeof(ReflectionCacheTests).GetMethod(nameof(GetMethod_MultipleCalls_ClearCache_DifferentObjects));
Assert.NotNull(mi2);
Expand All @@ -53,12 +53,12 @@ public void GetMethod_MultipleCalls_ClearCache_DifferentObjects(bool justSpecifi
Assert.NotSame(mi1, mi2);
}

private static Action<Type[]> GetBeforeUpdateMethod()
private static Action<Type[]> GetClearCacheMethod()
{
Type updateHandler = typeof(Type).Assembly.GetType("System.Reflection.Metadata.RuntimeTypeMetadataUpdateHandler", throwOnError: true, ignoreCase: false);
MethodInfo beforeUpdate = updateHandler.GetMethod("BeforeUpdate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, new[] { typeof(Type[]) });
Assert.NotNull(beforeUpdate);
return beforeUpdate.CreateDelegate<Action<Type[]>>();
MethodInfo clearCache = updateHandler.GetMethod("ClearCache", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, new[] { typeof(Type[]) });
Assert.NotNull(clearCache);
return clearCache.CreateDelegate<Action<Type[]>>();
}
}
}
Expand Up @@ -12,7 +12,7 @@ namespace System.Text.Json
/// <summary>Handler used to clear JsonSerializerOptions reflection cache upon a metadata update.</summary>
internal static class JsonSerializerOptionsUpdateHandler
{
public static void BeforeUpdate(Type[]? types)
public static void ClearCache(Type[]? types)
{
// Ignore the types, and just clear out all reflection caches from serializer options.
foreach (KeyValuePair<JsonSerializerOptions, object?> options in JsonSerializerOptions.TrackedOptionsInstances.All)
Expand Down
Expand Up @@ -211,7 +211,7 @@ public static async Task JsonSerializerOptionsUpdateHandler_ClearingDoesntPreven
{
// This test uses reflection to:
// - Access JsonSerializerOptions._classes
// - Access JsonSerializerOptionsUpdateHandler.BeforeUpdate
// - Access JsonSerializerOptionsUpdateHandler.ClearCache
//
// If either of them changes, this test will need to be kept in sync.

Expand All @@ -228,8 +228,9 @@ public static async Task JsonSerializerOptionsUpdateHandler_ClearingDoesntPreven
Assert.NotEqual(0, classes.Count);

Type updateHandler = typeof(JsonSerializerOptions).Assembly.GetType("System.Text.Json.JsonSerializerOptionsUpdateHandler", throwOnError: true, ignoreCase: false);
MethodInfo beforeUpdate = updateHandler.GetMethod("BeforeUpdate");
beforeUpdate.Invoke(null, new object[] { null });
MethodInfo clearCache = updateHandler.GetMethod("ClearCache");
Assert.NotNull(clearCache);
clearCache.Invoke(null, new object[] { null });
Assert.Equal(0, classes.Count);

await JsonSerializer.SerializeAsync<SimpleTestClass>(new MemoryStream(), testObj, options);
Expand Down

0 comments on commit 594e87a

Please sign in to comment.