diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/RuntimeTypeMetadataUpdateHandler.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/RuntimeTypeMetadataUpdateHandler.cs
index e45e84efb60f01..9c54c27f28b41a 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/RuntimeTypeMetadataUpdateHandler.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/RuntimeTypeMetadataUpdateHandler.cs
@@ -3,6 +3,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection.Metadata;
+using System.Runtime.CompilerServices;
[assembly: MetadataUpdateHandler(typeof(RuntimeTypeMetadataUpdateHandler))]
@@ -11,11 +12,21 @@ namespace System.Reflection.Metadata
/// Metadata update handler used to clear a Type's reflection cache in response to a metadata update notification.
internal static class RuntimeTypeMetadataUpdateHandler
{
+ ///
+ /// True to enable filtering deleted members from Reflection results. Set after the first metadata update.
+ ///
+ internal static bool FilterDeletedMembers { get; private set; }
+
+ internal static bool IsMetadataUpdateDeleted(RuntimeModule module, int memberToken)
+ => CustomAttribute.IsCustomAttributeDefined(module, memberToken, (RuntimeType)typeof(MetadataUpdateDeletedAttribute));
+
/// Clear type caches in response to an update notification.
/// The specific types to be cleared, or null to clear everything.
[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 ClearCache(Type[]? types)
{
+ FilterDeletedMembers = true;
+
if (RequiresClearingAllTypes(types))
{
// TODO: This should ideally be in a QCall in the runtime. As written here:
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs
index 96ecf9cf0cdcbb..c3d3fef4031a69 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs
@@ -1407,7 +1407,7 @@ internal static bool IsAttributeDefined(RuntimeModule decoratedModule, int decor
return IsCustomAttributeDefined(decoratedModule, decoratedMetadataToken, null, attributeCtorToken, false);
}
- private static bool IsCustomAttributeDefined(
+ internal static bool IsCustomAttributeDefined(
RuntimeModule decoratedModule, int decoratedMetadataToken, RuntimeType? attributeFilterType)
{
return IsCustomAttributeDefined(decoratedModule, decoratedMetadataToken, attributeFilterType, 0, false);
diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
index 19b63c1574d750..c5eee29b261ac1 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
@@ -1115,7 +1115,7 @@ internal static int GetSlot(IRuntimeMethodInfo method)
}
[MethodImpl(MethodImplOptions.InternalCall)]
- private static extern int GetMethodDef(RuntimeMethodHandleInternal method);
+ internal static extern int GetMethodDef(RuntimeMethodHandleInternal method);
internal static int GetMethodDef(IRuntimeMethodInfo method)
{
@@ -1566,7 +1566,7 @@ internal static ref byte GetFieldDataReference(ref byte target, RuntimeFieldInfo
private static unsafe partial void GetFieldDataReference(IntPtr fieldDesc, ObjectHandleOnStack target, ByteRefOnStack fieldDataRef);
[MethodImpl(MethodImplOptions.InternalCall)]
- private static extern int GetToken(IntPtr fieldDesc);
+ internal static extern int GetToken(IntPtr fieldDesc);
internal static int GetToken(RtFieldInfo field)
{
diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
index cd790f29de209c..e60729f2925b05 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
@@ -6,7 +6,9 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
+using System.IO;
using System.Reflection;
+using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
@@ -605,6 +607,13 @@ private unsafe RuntimeMethodInfo[] PopulateMethods(Filter filter)
continue;
#endregion
+ if (MetadataUpdater.IsSupported &&
+ RuntimeTypeMetadataUpdateHandler.FilterDeletedMembers &&
+ RuntimeTypeMetadataUpdateHandler.IsMetadataUpdateDeleted(declaringType.GetRuntimeModule(), RuntimeMethodHandle.GetMethodDef(methodHandle)))
+ {
+ continue;
+ }
+
#region Calculate Binding Flags
bool isPublic = (methodAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public;
bool isStatic = (methodAttributes & MethodAttributes.Static) != 0;
@@ -615,7 +624,7 @@ private unsafe RuntimeMethodInfo[] PopulateMethods(Filter filter)
RuntimeMethodHandleInternal instantiatedHandle = RuntimeMethodHandle.GetStubIfNeeded(methodHandle, declaringType, null);
RuntimeMethodInfo runtimeMethodInfo = new RuntimeMethodInfo(
- instantiatedHandle, declaringType, m_runtimeTypeCache, methodAttributes, bindingFlags, null);
+ instantiatedHandle, declaringType, m_runtimeTypeCache, methodAttributes, bindingFlags, null);
list.Add(runtimeMethodInfo);
#endregion
@@ -685,6 +694,14 @@ private unsafe RuntimeMethodInfo[] PopulateMethods(Filter filter)
#endregion
+ // Filter out deleted method before setting override state, so that a deleted override in a subclass does not hide override in an ancestor.
+ if (MetadataUpdater.IsSupported &&
+ RuntimeTypeMetadataUpdateHandler.FilterDeletedMembers &&
+ RuntimeTypeMetadataUpdateHandler.IsMetadataUpdateDeleted(declaringType.GetRuntimeModule(), RuntimeMethodHandle.GetMethodDef(methodHandle)))
+ {
+ continue;
+ }
+
#region Continue if this is a virtual and is already overridden
if (isVirtual)
{
@@ -719,7 +736,7 @@ private unsafe RuntimeMethodInfo[] PopulateMethods(Filter filter)
RuntimeMethodHandleInternal instantiatedHandle = RuntimeMethodHandle.GetStubIfNeeded(methodHandle, declaringType, null);
RuntimeMethodInfo runtimeMethodInfo = new RuntimeMethodInfo(
- instantiatedHandle, declaringType, m_runtimeTypeCache, methodAttributes, bindingFlags, null);
+ instantiatedHandle, declaringType, m_runtimeTypeCache, methodAttributes, bindingFlags, null);
list.Add(runtimeMethodInfo);
#endregion
@@ -764,6 +781,13 @@ private RuntimeConstructorInfo[] PopulateConstructors(Filter filter)
(methodAttributes & MethodAttributes.Abstract) == 0 &&
(methodAttributes & MethodAttributes.Virtual) == 0);
+ if (MetadataUpdater.IsSupported &&
+ RuntimeTypeMetadataUpdateHandler.FilterDeletedMembers &&
+ RuntimeTypeMetadataUpdateHandler.IsMetadataUpdateDeleted(declaringType.GetRuntimeModule(), RuntimeMethodHandle.GetMethodDef(methodHandle)))
+ {
+ continue;
+ }
+
#region Calculate Binding Flags
bool isPublic = (methodAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public;
bool isStatic = (methodAttributes & MethodAttributes.Static) != 0;
@@ -875,6 +899,13 @@ private void PopulateRtFields(Filter filter,
continue;
}
+ if (MetadataUpdater.IsSupported &&
+ RuntimeTypeMetadataUpdateHandler.FilterDeletedMembers &&
+ RuntimeTypeMetadataUpdateHandler.IsMetadataUpdateDeleted(declaringType.GetRuntimeModule(), RuntimeFieldHandle.GetToken(handle)))
+ {
+ continue;
+ }
+
#region Calculate Binding Flags
bool isPublic = fieldAccess == FieldAttributes.Public;
bool isStatic = (fieldAttributes & FieldAttributes.Static) != 0;
@@ -885,8 +916,7 @@ private void PopulateRtFields(Filter filter,
if (needsStaticFieldForGeneric && isStatic)
runtimeFieldHandle = RuntimeFieldHandle.GetStaticFieldForGenericType(runtimeFieldHandle, declaringType);
- RuntimeFieldInfo runtimeFieldInfo =
- new RtFieldInfo(runtimeFieldHandle, declaringType, m_runtimeTypeCache, bindingFlags);
+ var runtimeFieldInfo = new RtFieldInfo(runtimeFieldHandle, declaringType, m_runtimeTypeCache, bindingFlags);
list.Add(runtimeFieldInfo);
}
@@ -903,8 +933,8 @@ private void PopulateLiteralFields(Filter filter, RuntimeType declaringType, ref
if (MdToken.IsNullToken(tkDeclaringType))
return;
- RuntimeModule module = declaringType.GetRuntimeModule();
- MetadataImport scope = module.MetadataImport;
+ RuntimeModule declaringModule = declaringType.GetRuntimeModule();
+ MetadataImport scope = declaringModule.MetadataImport;
scope.EnumFields(tkDeclaringType, out MetadataEnumResult tkFields);
@@ -936,6 +966,13 @@ private void PopulateLiteralFields(Filter filter, RuntimeType declaringType, ref
continue;
}
+ if (MetadataUpdater.IsSupported &&
+ RuntimeTypeMetadataUpdateHandler.FilterDeletedMembers &&
+ RuntimeTypeMetadataUpdateHandler.IsMetadataUpdateDeleted(declaringModule, tkField))
+ {
+ continue;
+ }
+
#region Calculate Binding Flags
bool isPublic = fieldAccess == FieldAttributes.Public;
bool isStatic = (fieldAttributes & FieldAttributes.Static) != 0;
@@ -948,7 +985,7 @@ private void PopulateLiteralFields(Filter filter, RuntimeType declaringType, ref
list.Add(runtimeFieldInfo);
}
}
- GC.KeepAlive(module);
+ GC.KeepAlive(declaringModule);
}
private void AddSpecialInterface(
@@ -1144,8 +1181,8 @@ private void PopulateEvents(
if (MdToken.IsNullToken(tkDeclaringType))
return;
- RuntimeModule module = declaringType.GetRuntimeModule();
- MetadataImport scope = module.MetadataImport;
+ RuntimeModule declaringModule = declaringType.GetRuntimeModule();
+ MetadataImport scope = declaringModule.MetadataImport;
scope.EnumEvents(tkDeclaringType, out MetadataEnumResult tkEvents);
@@ -1164,6 +1201,13 @@ private void PopulateEvents(
continue;
}
+ if (MetadataUpdater.IsSupported &&
+ RuntimeTypeMetadataUpdateHandler.FilterDeletedMembers &&
+ RuntimeTypeMetadataUpdateHandler.IsMetadataUpdateDeleted(declaringModule, tkEvent))
+ {
+ continue;
+ }
+
RuntimeEventInfo eventInfo = new RuntimeEventInfo(
tkEvent, declaringType, m_runtimeTypeCache, out bool isPrivate);
@@ -1191,7 +1235,7 @@ private void PopulateEvents(
list.Add(eventInfo);
}
- GC.KeepAlive(module);
+ GC.KeepAlive(declaringModule);
}
private RuntimePropertyInfo[] PopulateProperties(Filter filter)
@@ -1251,8 +1295,8 @@ private void PopulateProperties(
if (MdToken.IsNullToken(tkDeclaringType))
return;
- RuntimeModule module = declaringType.GetRuntimeModule();
- MetadataImport scope = module.MetadataImport;
+ RuntimeModule declaringModule = declaringType.GetRuntimeModule();
+ MetadataImport scope = declaringModule.MetadataImport;
scope.EnumProperties(tkDeclaringType, out MetadataEnumResult tkProperties);
@@ -1276,6 +1320,14 @@ private void PopulateProperties(
continue;
}
+ // Filter out deleted property before updating usedSlots, so that a deleted override in a subclass does not hide override in an ancestor.
+ if (MetadataUpdater.IsSupported &&
+ RuntimeTypeMetadataUpdateHandler.FilterDeletedMembers &&
+ RuntimeTypeMetadataUpdateHandler.IsMetadataUpdateDeleted(declaringModule, tkProperty))
+ {
+ continue;
+ }
+
RuntimePropertyInfo propertyInfo =
new RuntimePropertyInfo(
tkProperty, declaringType, m_runtimeTypeCache, out bool isPrivate);
@@ -1365,7 +1417,7 @@ private void PopulateProperties(
list.Add(propertyInfo);
}
- GC.KeepAlive(module);
+ GC.KeepAlive(declaringModule);
}
#endregion
@@ -1458,6 +1510,7 @@ private MemberInfoCache GetMemberCache(ref MemberInfoCache? m_cache)
return existingCache;
}
+
#endregion
#region Internal Members
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index e84524dc4ff9c3..f3f528efd1b258 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -886,6 +886,7 @@
+
@@ -2915,4 +2916,4 @@
-
+
\ No newline at end of file
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/MetadataUpdateDeletedAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/MetadataUpdateDeletedAttribute.cs
new file mode 100644
index 00000000000000..32690b0fbece53
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/MetadataUpdateDeletedAttribute.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Runtime.CompilerServices;
+
+///
+/// This attribute is emitted by the compiler when a metadata entity is deleted during a
+/// Hot Reload session.
+///
+[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)]
+public sealed class MetadataUpdateDeletedAttribute : Attribute;
diff --git a/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs b/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs
index 065c19d3626247..8f7555ac5b599d 100644
--- a/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs
+++ b/src/libraries/System.Runtime.Loader/ref/System.Runtime.Loader.cs
@@ -32,6 +32,11 @@ public sealed partial class CreateNewOnMetadataUpdateAttribute : System.Attribut
{
public CreateNewOnMetadataUpdateAttribute() { }
}
+ [System.AttributeUsageAttribute(System.AttributeTargets.All, AllowMultiple=false, Inherited=false)]
+ public sealed partial class MetadataUpdateDeletedAttribute : System.Attribute
+ {
+ public MetadataUpdateDeletedAttribute() { }
+ }
[System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple=false, Inherited=false)]
public partial class MetadataUpdateOriginalTypeAttribute : System.Attribute
{
diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember/ReflectionDeleteMember.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember/ReflectionDeleteMember.cs
new file mode 100644
index 00000000000000..8b858b8d109357
--- /dev/null
+++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember/ReflectionDeleteMember.cs
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Reflection.Metadata.ApplyUpdate.Test
+{
+ public class ReflectionDeleteMember
+ {
+ public int F1;
+
+ public ReflectionDeleteMember() { }
+ public ReflectionDeleteMember(int arg) { F1 = arg; }
+
+ public virtual int P1 { get; set; }
+ public virtual void M1() { }
+
+ public virtual int P2 { get; }
+ public virtual void M2() { }
+
+ public int P3 { get; set; }
+ public void M3() { }
+ public event Action E1 { add { } remove { } }
+ }
+
+ public class ReflectionDeleteMember_Derived : ReflectionDeleteMember
+ {
+ public new int F1;
+
+ public override int P1 { get; set; }
+ public override void M1() { }
+
+ public override int P2 { get; }
+ public override void M2() { }
+
+ public new int P3 { get; set; }
+ public new void M3() { }
+ public new event Action E1 { add { } remove { } }
+ }
+
+ public class ReflectionDeleteMember_Derived2 : ReflectionDeleteMember_Derived
+ {
+ public override int P1 { get; set; }
+ public override void M1() { }
+
+ public override int P2 { get; }
+ public override void M2() { }
+ }
+
+ public interface IReflectionDeleteMember
+ {
+ int P1 { get; set; }
+ int P2 { get; }
+ void M1();
+ void M2();
+ event Action E1;
+ event Action E2;
+ }
+}
+
diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember/ReflectionDeleteMember_v1.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember/ReflectionDeleteMember_v1.cs
new file mode 100644
index 00000000000000..6d47382c684fef
--- /dev/null
+++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember/ReflectionDeleteMember_v1.cs
@@ -0,0 +1,57 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Reflection.Metadata.ApplyUpdate.Test
+{
+ public class ReflectionDeleteMember
+ {
+ public int F1; // delete: not supported by Roslyn
+
+ public ReflectionDeleteMember() { }
+ // delete: public ReflectionDeleteMember(int arg) { F1 = arg; }
+
+ public virtual int P1 { get; set; }
+ public virtual void M1() { }
+
+ public virtual int P2 { get; }
+ public virtual void M2() { }
+
+ public int P3 { get; set; }
+ public void M3() { }
+ public event Action E1 { add { } remove { } }
+ }
+
+ public class ReflectionDeleteMember_Derived : ReflectionDeleteMember
+ {
+ public new int F1;
+
+ public override int P1 { get; set; } // delete: not supported by Roslyn
+ public override void M1() { } // delete: not supported by Roslyn
+
+ public override int P2 { get; }
+ public override void M2() { }
+
+ // delete: public new int P3 { get; set; }
+ // delete: public new void M3() { }
+ // delete: public new event Action E1 { add { } remove { } }
+ }
+
+ public class ReflectionDeleteMember_Derived2 : ReflectionDeleteMember_Derived
+ {
+ public override int P1 { get; set; }
+ public override void M1() { }
+
+ public override int P2 { get; } // delete: not supported by Roslyn
+ public override void M2() { } // delete: not supported by Roslyn
+ }
+
+ public interface IReflectionDeleteMember
+ {
+ int P1 { get; set; }
+ int P2 { get; } // delete: not supported by Roslyn
+ void M1();
+ void M2(); // delete: not supported by Roslyn
+ event Action E1;
+ event Action E2; // delete: not supported by Roslyn
+ }
+}
diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember.csproj b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember.csproj
new file mode 100644
index 00000000000000..fbd7d2323ed2b5
--- /dev/null
+++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember.csproj
@@ -0,0 +1,11 @@
+
+
+ System.Runtime.Loader.Tests
+ $(NetCoreAppCurrent)
+ true
+ deltascript.json
+
+
+
+
+
diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember/deltascript.json b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember/deltascript.json
new file mode 100644
index 00000000000000..acd60b7974dfe8
--- /dev/null
+++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdate/System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember/deltascript.json
@@ -0,0 +1,6 @@
+{
+ "changes": [
+ {"document": "ReflectionDeleteMember.cs", "update": "ReflectionDeleteMember_v1.cs"}
+ ]
+}
+
diff --git a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs
index 98475ed8a5e21e..d5986e07cc3eb9 100644
--- a/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs
+++ b/src/libraries/System.Runtime.Loader/tests/ApplyUpdateTest.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Collections.Generic;
+using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -19,6 +21,119 @@ namespace System.Reflection.Metadata
[Collection(nameof(DisableParallelization))]
public class ApplyUpdateTest
{
+
+ [ConditionalFact(typeof(ApplyUpdateUtil), nameof (ApplyUpdateUtil.IsSupported))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/120547", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoRuntime))]
+ void DeleteMemberTest()
+ {
+ ApplyUpdateUtil.TestCase(static () =>
+ {
+ var assm = typeof (ApplyUpdate.Test.ReflectionDeleteMember).Assembly;
+
+ var type_ReflectionDeleteMember = assm.GetType("System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember");
+ var type_ReflectionDeleteMember_Derived = assm.GetType("System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember_Derived");
+ var type_ReflectionDeleteMember_Derived2 = assm.GetType("System.Reflection.Metadata.ApplyUpdate.Test.ReflectionDeleteMember_Derived2");
+ var type_IReflectionDeleteMember = assm.GetType("System.Reflection.Metadata.ApplyUpdate.Test.IReflectionDeleteMember");
+
+ ApplyUpdateUtil.ApplyUpdate(assm);
+ ApplyUpdateUtil.ClearAllReflectionCaches();
+
+ Assert.Equal(
+ [
+ "get_P1 : ReflectionDeleteMember",
+ "set_P1 : ReflectionDeleteMember",
+ "M1 : ReflectionDeleteMember",
+ "get_P2 : ReflectionDeleteMember",
+ "M2 : ReflectionDeleteMember",
+ "get_P3 : ReflectionDeleteMember",
+ "set_P3 : ReflectionDeleteMember",
+ "M3 : ReflectionDeleteMember",
+ "add_E1 : ReflectionDeleteMember",
+ "remove_E1 : ReflectionDeleteMember",
+ "GetType : Object",
+ "ToString : Object",
+ "Equals : Object",
+ "GetHashCode : Object",
+ ".ctor(0) : ReflectionDeleteMember",
+ "P1 : ReflectionDeleteMember",
+ "P2 : ReflectionDeleteMember",
+ "P3 : ReflectionDeleteMember",
+ "E1 : ReflectionDeleteMember",
+ "F1 : ReflectionDeleteMember",
+ ], Inspect(type_ReflectionDeleteMember));
+
+ Assert.Equal(
+ [
+ "get_P1 : ReflectionDeleteMember_Derived",
+ "set_P1 : ReflectionDeleteMember_Derived",
+ "M1 : ReflectionDeleteMember_Derived",
+ "get_P2 : ReflectionDeleteMember_Derived",
+ "M2 : ReflectionDeleteMember_Derived",
+ "get_P3 : ReflectionDeleteMember",
+ "set_P3 : ReflectionDeleteMember",
+ "M3 : ReflectionDeleteMember",
+ "add_E1 : ReflectionDeleteMember",
+ "remove_E1 : ReflectionDeleteMember",
+ "GetType : Object",
+ "ToString : Object",
+ "Equals : Object",
+ "GetHashCode : Object",
+ ".ctor(0) : ReflectionDeleteMember_Derived",
+ "P1 : ReflectionDeleteMember_Derived",
+ "P2 : ReflectionDeleteMember_Derived",
+ "P3 : ReflectionDeleteMember",
+ "E1 : ReflectionDeleteMember",
+ "F1 : ReflectionDeleteMember_Derived",
+ "F1 : ReflectionDeleteMember",
+ ], Inspect(type_ReflectionDeleteMember_Derived));
+
+ Assert.Equal(
+ [
+ "get_P1 : ReflectionDeleteMember_Derived2",
+ "set_P1 : ReflectionDeleteMember_Derived2",
+ "M1 : ReflectionDeleteMember_Derived2",
+ "get_P2 : ReflectionDeleteMember_Derived2",
+ "M2 : ReflectionDeleteMember_Derived2",
+ "get_P3 : ReflectionDeleteMember",
+ "set_P3 : ReflectionDeleteMember",
+ "M3 : ReflectionDeleteMember",
+ "add_E1 : ReflectionDeleteMember",
+ "remove_E1 : ReflectionDeleteMember",
+ "GetType : Object",
+ "ToString : Object",
+ "Equals : Object",
+ "GetHashCode : Object",
+ ".ctor(0) : ReflectionDeleteMember_Derived2",
+ "P1 : ReflectionDeleteMember_Derived2",
+ "P2 : ReflectionDeleteMember_Derived2",
+ "P3 : ReflectionDeleteMember",
+ "E1 : ReflectionDeleteMember",
+ "F1 : ReflectionDeleteMember_Derived",
+ "F1 : ReflectionDeleteMember",
+ ], Inspect(type_ReflectionDeleteMember_Derived2));
+
+ Assert.Equal(
+ [
+ "get_P1 : IReflectionDeleteMember",
+ "set_P1 : IReflectionDeleteMember",
+ "get_P2 : IReflectionDeleteMember",
+ "M1 : IReflectionDeleteMember",
+ "M2 : IReflectionDeleteMember",
+ "add_E1 : IReflectionDeleteMember",
+ "remove_E1 : IReflectionDeleteMember",
+ "add_E2 : IReflectionDeleteMember",
+ "remove_E2 : IReflectionDeleteMember",
+ "P1 : IReflectionDeleteMember",
+ "P2 : IReflectionDeleteMember",
+ "E1 : IReflectionDeleteMember",
+ "E2 : IReflectionDeleteMember",
+ ], Inspect(type_IReflectionDeleteMember));
+ });
+
+ static IEnumerable Inspect(Type type)
+ => type.GetMembers().Select(m => (m is ConstructorInfo ctor ? $"{ctor.Name}({ctor.GetParameters().Length})" : m.Name) + " : " + m.DeclaringType!.Name);
+ }
+
[ConditionalFact(typeof(ApplyUpdateUtil), nameof (ApplyUpdateUtil.IsSupported))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/54617", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))]
void StaticMethodBodyUpdate()
@@ -626,7 +741,7 @@ internal static Type CheckReflectedType(Assembly assm, Type[] allTypes, string n
if (nestedIdx != -1)
comparisonName = typeName.Substring(nestedIdx+1);
Assert.True(comparisonName == ty.Name, $"{callerFilePath}:{callerLineNumber}: returned type has unexpected name '{ty.Name}' (expected: '{comparisonName}') in {callerMemberName}");
- Assert.True(ContainsTypeWithName (allTypes, fullName), $"{callerFilePath}:{callerLineNumber}: expected Assembly.GetTypes to contain '{fullName}', but it didn't in {callerMemberName}");
+ Assert.True(ContainsTypeWithName (allTypes, fullName), $"{callerFilePath}:{callerLineNumber}: expected Assembly.GetTypes to contain '{fullName}', but it didn'type_ReflectionDeleteMember in {callerMemberName}");
if (moreChecks != null)
moreChecks(ty);
return ty;
diff --git a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj
index f9f54c521f0240..c557f7e3b4a05b 100644
--- a/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj
+++ b/src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj
@@ -40,6 +40,11 @@
System.Reflection.Metadata.ApplyUpdate.Test.
+
+
+
+
+
@@ -74,6 +79,7 @@
+