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 @@ +