From cf7e7a46f8a4a6225a8f1e059a846ccdebf0454c Mon Sep 17 00:00:00 2001 From: Eric Erhardt Date: Tue, 15 Jun 2021 16:14:10 -0500 Subject: [PATCH] Add Type.GetMemberWithSameMetadataDefinitionAs (#53704) * Add API to find MethodInfo on instantiated generic type from generic type definition Fix #45771 * Rename to GetMemberWithSameMetadataDefinitionAs * Fix a bug for NestedType * Use new method libraries that were working around not having it * Implement GetMemberWithSameMetadataDefinitionAs in mono * Revert JavaScript Runtime changes. * Support inheritance in GetMemberWithSameMetadataDefinitionAs. --- .../src/System/RuntimeType.CoreCLR.cs | 125 +++++++++++++++++++ .../src/System/ComponentModel/NullableConverter.cs | 12 +- .../src/System/Dynamic/Utils/TypeUtils.cs | 10 +- .../src/System/Linq/Expressions/Compiler/ILGen.cs | 25 ++-- .../Expressions/Compiler/LambdaCompiler.Binary.cs | 2 +- .../Compiler/LambdaCompiler.Expressions.cs | 12 +- .../Expressions/Compiler/LambdaCompiler.Unary.cs | 4 +- .../System/Linq/Expressions/LambdaExpression.cs | 17 +-- .../src/Resources/Strings.resx | 3 + .../src/System/Reflection/TypeDelegator.cs | 2 + .../System.Private.CoreLib/src/System/Type.cs | 32 +++++ .../System.Reflection/tests/TypeInfoTests.cs | 114 +++++++++++++++++ .../System/Runtime/Serialization/ObjectManager.cs | 7 +- src/libraries/System.Runtime/ref/System.Runtime.cs | 1 + .../src/System/Reflection/RuntimeEventInfo.cs | 8 -- .../src/System/Reflection/RuntimeFieldInfo.cs | 8 -- .../src/System/Reflection/RuntimeMethodInfo.cs | 16 --- .../src/System/Reflection/RuntimePropertyInfo.cs | 8 -- .../src/System/RuntimeType.Mono.cs | 138 +++++++++++++++++++-- 19 files changed, 436 insertions(+), 108 deletions(-) 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 1e2ece2..b07971e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3068,6 +3068,131 @@ namespace System return compressMembers; } + + public override MemberInfo GetMemberWithSameMetadataDefinitionAs(MemberInfo member) + { + if (member is null) throw new ArgumentNullException(nameof(member)); + + RuntimeType? runtimeType = this; + while (runtimeType != null) + { + MemberInfo? result = member.MemberType switch + { + MemberTypes.Method => GetMethodWithSameMetadataDefinitionAs(runtimeType, member), + MemberTypes.Constructor => GetConstructorWithSameMetadataDefinitionAs(runtimeType, member), + MemberTypes.Property => GetPropertyWithSameMetadataDefinitionAs(runtimeType, member), + MemberTypes.Field => GetFieldWithSameMetadataDefinitionAs(runtimeType, member), + MemberTypes.Event => GetEventWithSameMetadataDefinitionAs(runtimeType, member), + MemberTypes.NestedType => GetNestedTypeWithSameMetadataDefinitionAs(runtimeType, member), + _ => null + }; + + if (result != null) + { + return result; + } + + runtimeType = runtimeType.GetBaseType(); + } + + throw CreateGetMemberWithSameMetadataDefinitionAsNotFoundException(member); + } + + private static MemberInfo? GetMethodWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo method) + { + RuntimeMethodInfo[] cache = runtimeType.Cache.GetMethodList(MemberListType.CaseSensitive, method.Name); + + for (int i = 0; i < cache.Length; i++) + { + RuntimeMethodInfo candidate = cache[i]; + if (candidate.HasSameMetadataDefinitionAs(method)) + { + return candidate; + } + } + + return null; + } + + private static MemberInfo? GetConstructorWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo constructor) + { + RuntimeConstructorInfo[] cache = runtimeType.Cache.GetConstructorList(MemberListType.CaseSensitive, constructor.Name); + + for (int i = 0; i < cache.Length; i++) + { + RuntimeConstructorInfo candidate = cache[i]; + if (candidate.HasSameMetadataDefinitionAs(constructor)) + { + return candidate; + } + } + + return null; + } + + private static MemberInfo? GetPropertyWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo property) + { + RuntimePropertyInfo[] cache = runtimeType.Cache.GetPropertyList(MemberListType.CaseSensitive, property.Name); + + for (int i = 0; i < cache.Length; i++) + { + RuntimePropertyInfo candidate = cache[i]; + if (candidate.HasSameMetadataDefinitionAs(property)) + { + return candidate; + } + } + + return null; + } + + private static MemberInfo? GetFieldWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo field) + { + RuntimeFieldInfo[] cache = runtimeType.Cache.GetFieldList(MemberListType.CaseSensitive, field.Name); + + for (int i = 0; i < cache.Length; i++) + { + RuntimeFieldInfo candidate = cache[i]; + if (candidate.HasSameMetadataDefinitionAs(field)) + { + return candidate; + } + } + + return null; + } + + private static MemberInfo? GetEventWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo eventInfo) + { + RuntimeEventInfo[] cache = runtimeType.Cache.GetEventList(MemberListType.CaseSensitive, eventInfo.Name); + + for (int i = 0; i < cache.Length; i++) + { + RuntimeEventInfo candidate = cache[i]; + if (candidate.HasSameMetadataDefinitionAs(eventInfo)) + { + return candidate; + } + } + + return null; + } + + private static MemberInfo? GetNestedTypeWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo nestedType) + { + RuntimeType[] cache = runtimeType.Cache.GetNestedTypeList(MemberListType.CaseSensitive, nestedType.Name); + + for (int i = 0; i < cache.Length; i++) + { + RuntimeType candidate = cache[i]; + if (candidate.HasSameMetadataDefinitionAs(nestedType)) + { + return candidate; + } + } + + return null; + } #endregion #region Identity diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NullableConverter.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NullableConverter.cs index 9bcfdc0..08a077b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NullableConverter.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/NullableConverter.cs @@ -15,6 +15,8 @@ namespace System.ComponentModel /// public class NullableConverter : TypeConverter { + private static readonly ConstructorInfo s_nullableConstructor = typeof(Nullable<>).GetConstructor(typeof(Nullable<>).GetGenericArguments())!; + /// /// Nullable converter is initialized with the underlying simple type. /// @@ -108,7 +110,7 @@ namespace System.ComponentModel } else if (destinationType == typeof(InstanceDescriptor)) { - ConstructorInfo ci = GetNullableConstructor(); + ConstructorInfo ci = (ConstructorInfo)NullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableConstructor); Debug.Assert(ci != null, "Couldn't find constructor"); return new InstanceDescriptor(ci, new object[] { value }, true); } @@ -128,14 +130,6 @@ namespace System.ComponentModel return base.ConvertTo(context, culture, value, destinationType); } - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(Nullable<>))] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern", - Justification = "The Nullable ctor will be preserved by the DynamicDependency.")] - private ConstructorInfo GetNullableConstructor() - { - return NullableType.GetConstructor(new Type[] { UnderlyingType })!; - } - /// /// public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) diff --git a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs index afa81a1..3a0fc56 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Dynamic/Utils/TypeUtils.cs @@ -16,6 +16,8 @@ namespace System.Dynamic.Utils .Select(i => i.GetGenericTypeDefinition()) .ToArray(); + private static readonly ConstructorInfo s_nullableConstructor = typeof(Nullable<>).GetConstructor(typeof(Nullable<>).GetGenericArguments())!; + public static Type GetNonNullableType(this Type type) => IsNullableType(type) ? type.GetGenericArguments()[0] : type; public static Type GetNullableType(this Type type) @@ -29,15 +31,11 @@ namespace System.Dynamic.Utils return type; } - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors, typeof(Nullable<>))] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", - Justification = "The Nullable ctor will be preserved by the DynamicDependency.")] - public static ConstructorInfo GetNullableConstructor(Type nullableType, Type nonNullableType) + public static ConstructorInfo GetNullableConstructor(Type nullableType) { Debug.Assert(nullableType.IsNullableType()); - Debug.Assert(!nonNullableType.IsNullableType() && nonNullableType.IsValueType); - return nullableType.GetConstructor(new Type[] { nonNullableType })!; + return (ConstructorInfo)nullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableConstructor); } public static bool IsNullableType(this Type type) => type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs index 673e213..5cd7fd5 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/ILGen.cs @@ -12,6 +12,10 @@ namespace System.Linq.Expressions.Compiler { internal static class ILGen { + private static readonly MethodInfo s_nullableHasValueGetter = typeof(Nullable<>).GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public)!; + private static readonly MethodInfo s_nullableValueGetter = typeof(Nullable<>).GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public)!; + private static readonly MethodInfo s_nullableGetValueOrDefault = typeof(Nullable<>).GetMethod("GetValueOrDefault", Type.EmptyTypes)!; + internal static void Emit(this ILGenerator il, OpCode opcode, MethodBase methodBase) { Debug.Assert(methodBase is MethodInfo || methodBase is ConstructorInfo); @@ -485,7 +489,7 @@ namespace System.Linq.Expressions.Compiler if (TryEmitILConstant(il, value, nonNullType)) { - il.Emit(OpCodes.Newobj, TypeUtils.GetNullableConstructor(type, nonNullType)); + il.Emit(OpCodes.Newobj, TypeUtils.GetNullableConstructor(type)); return true; } @@ -826,7 +830,7 @@ namespace System.Linq.Expressions.Compiler Type nnTypeTo = typeTo.GetNonNullableType(); il.EmitConvertToType(nnTypeFrom, nnTypeTo, isChecked, locals); // construct result type - ConstructorInfo ci = TypeUtils.GetNullableConstructor(typeTo, nnTypeTo); + ConstructorInfo ci = TypeUtils.GetNullableConstructor(typeTo); il.Emit(OpCodes.Newobj, ci); labEnd = il.DefineLabel(); il.Emit(OpCodes.Br_S, labEnd); @@ -846,7 +850,7 @@ namespace System.Linq.Expressions.Compiler Debug.Assert(typeTo.IsNullableType()); Type nnTypeTo = typeTo.GetNonNullableType(); il.EmitConvertToType(typeFrom, nnTypeTo, isChecked, locals); - ConstructorInfo ci = TypeUtils.GetNullableConstructor(typeTo, nnTypeTo); + ConstructorInfo ci = TypeUtils.GetNullableConstructor(typeTo); il.Emit(OpCodes.Newobj, ci); } @@ -898,38 +902,29 @@ namespace System.Linq.Expressions.Compiler il.EmitNonNullableToNullableConversion(typeFrom, typeTo, isChecked, locals); } - [DynamicDependency("get_HasValue", typeof(Nullable<>))] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", - Justification = "The Nullable method will be preserved by the DynamicDependency.")] internal static void EmitHasValue(this ILGenerator il, Type nullableType) { Debug.Assert(nullableType.IsNullableType()); - MethodInfo mi = nullableType.GetMethod("get_HasValue", BindingFlags.Instance | BindingFlags.Public)!; + MethodInfo mi = (MethodInfo)nullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableHasValueGetter); Debug.Assert(nullableType.IsValueType); il.Emit(OpCodes.Call, mi); } - [DynamicDependency("get_Value", typeof(Nullable<>))] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", - Justification = "The Nullable method will be preserved by the DynamicDependency.")] internal static void EmitGetValue(this ILGenerator il, Type nullableType) { Debug.Assert(nullableType.IsNullableType()); - MethodInfo mi = nullableType.GetMethod("get_Value", BindingFlags.Instance | BindingFlags.Public)!; + MethodInfo mi = (MethodInfo)nullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableValueGetter); Debug.Assert(nullableType.IsValueType); il.Emit(OpCodes.Call, mi); } - [DynamicDependency("GetValueOrDefault()", typeof(Nullable<>))] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", - Justification = "The Nullable method will be preserved by the DynamicDependency.")] internal static void EmitGetValueOrDefault(this ILGenerator il, Type nullableType) { Debug.Assert(nullableType.IsNullableType()); - MethodInfo mi = nullableType.GetMethod("GetValueOrDefault", Type.EmptyTypes)!; + MethodInfo mi = (MethodInfo)nullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableGetValueOrDefault); Debug.Assert(nullableType.IsValueType); il.Emit(OpCodes.Call, mi); } diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Binary.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Binary.cs index a888be0..c4e448a 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Binary.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Binary.cs @@ -480,7 +480,7 @@ namespace System.Linq.Expressions.Compiler EmitBinaryOperator(op, leftType.GetNonNullableType(), rightType.GetNonNullableType(), resultNonNullableType, liftedToNull: false); // construct result type - ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType, resultNonNullableType); + ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType); _ilg.Emit(OpCodes.Newobj, ci); _ilg.Emit(OpCodes.Stloc, locResult); _ilg.Emit(OpCodes.Br_S, labEnd); diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Expressions.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Expressions.cs index a3049f8..505b298 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Expressions.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Expressions.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Dynamic.Utils; using System.Reflection; using System.Reflection.Emit; @@ -14,6 +13,8 @@ namespace System.Linq.Expressions.Compiler { internal sealed partial class LambdaCompiler { + private static readonly FieldInfo s_callSiteTargetField = typeof(CallSite<>).GetField("Target")!; + [Flags] internal enum CompilationFlags { @@ -613,13 +614,10 @@ namespace System.Linq.Expressions.Compiler EmitWriteBack(wb); } - [DynamicDependency("Target", typeof(CallSite<>))] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", - Justification = "The 'Target' field will be preserved by the DynamicDependency.")] private static FieldInfo GetCallSiteTargetField(Type siteType) { Debug.Assert(siteType.IsGenericType && siteType.GetGenericTypeDefinition() == typeof(CallSite<>)); - return siteType.GetField("Target")!; + return (FieldInfo)siteType.GetMemberWithSameMetadataDefinitionAs(s_callSiteTargetField); } private void EmitNewExpression(Expression expr) @@ -1171,7 +1169,7 @@ namespace System.Linq.Expressions.Compiler EmitMethodCallExpression(mc); if (resultType.IsNullableType() && !TypeUtils.AreEquivalent(resultType, mc.Type)) { - ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType, mc.Type); + ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType); _ilg.Emit(OpCodes.Newobj, ci); } _ilg.Emit(OpCodes.Br_S, exit); @@ -1275,7 +1273,7 @@ namespace System.Linq.Expressions.Compiler EmitMethodCallExpression(mc); if (resultType.IsNullableType() && !TypeUtils.AreEquivalent(resultType, mc.Type)) { - ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType, mc.Type); + ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType); _ilg.Emit(OpCodes.Newobj, ci); } _ilg.Emit(OpCodes.Br_S, exit); diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Unary.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Unary.cs index f487d73..b17d414 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Unary.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Compiler/LambdaCompiler.Unary.cs @@ -94,7 +94,7 @@ namespace System.Linq.Expressions.Compiler EmitBinaryOperator(ExpressionType.SubtractChecked, nnType, nnType, nnType, liftedToNull: false); // construct result - _ilg.Emit(OpCodes.Newobj, TypeUtils.GetNullableConstructor(type, nnType)); + _ilg.Emit(OpCodes.Newobj, TypeUtils.GetNullableConstructor(type)); _ilg.Emit(OpCodes.Br_S, end); // if null then push back on stack @@ -164,7 +164,7 @@ namespace System.Linq.Expressions.Compiler EmitUnaryOperator(op, nnOperandType, nnOperandType); // construct result - ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType, nnOperandType); + ConstructorInfo ci = TypeUtils.GetNullableConstructor(resultType); _ilg.Emit(OpCodes.Newobj, ci); _ilg.Emit(OpCodes.Br_S, labEnd); diff --git a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs index fc45b9c..a615e89 100644 --- a/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs +++ b/src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/LambdaExpression.cs @@ -21,6 +21,8 @@ namespace System.Linq.Expressions [DebuggerTypeProxy(typeof(LambdaExpressionProxy))] public abstract class LambdaExpression : Expression, IParameterProvider { + private static readonly MethodInfo s_expressionCompileMethodInfo = typeof(Expression<>).GetMethod("Compile", Type.EmptyTypes)!; + private readonly Expression _body; internal LambdaExpression(Expression body) @@ -119,20 +121,7 @@ namespace System.Linq.Expressions return typeof(LambdaExpression).GetMethod("Compile", Type.EmptyTypes)!; } - return GetDerivedCompileMethod(lambdaExpressionType); - } - - [DynamicDependency("Compile()", typeof(Expression<>))] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", - Justification = "The 'Compile' method will be preserved by the DynamicDependency.")] - private static MethodInfo GetDerivedCompileMethod(Type lambdaExpressionType) - { - Debug.Assert(lambdaExpressionType.IsAssignableTo(typeof(LambdaExpression)) && lambdaExpressionType != typeof(LambdaExpression)); - - MethodInfo result = lambdaExpressionType.GetMethod("Compile", Type.EmptyTypes)!; - Debug.Assert(result.DeclaringType!.IsGenericType && result.DeclaringType.GetGenericTypeDefinition() == typeof(Expression<>)); - - return result; + return (MethodInfo)lambdaExpressionType.GetMemberWithSameMetadataDefinitionAs(s_expressionCompileMethodInfo); } /// diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index c64c59a..99b0321 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3781,4 +3781,7 @@ File encryption is not supported on this platform. + + A MemberInfo that matches '{0}' could not be found. + \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeDelegator.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeDelegator.cs index f09c20a..d9de502 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeDelegator.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/TypeDelegator.cs @@ -126,6 +126,8 @@ namespace System.Reflection [DynamicallyAccessedMembers(GetAllMembers)] public override MemberInfo[] GetMembers(BindingFlags bindingAttr) => typeImpl.GetMembers(bindingAttr); + public override MemberInfo GetMemberWithSameMetadataDefinitionAs(MemberInfo member) => typeImpl.GetMemberWithSameMetadataDefinitionAs(member); + protected override TypeAttributes GetAttributeFlagsImpl() => typeImpl.Attributes; public override bool IsTypeDefinition => typeImpl.IsTypeDefinition; diff --git a/src/libraries/System.Private.CoreLib/src/System/Type.cs b/src/libraries/System.Private.CoreLib/src/System/Type.cs index ca57ed5..3c1a178 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Type.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Type.cs @@ -226,6 +226,38 @@ namespace System DynamicallyAccessedMemberTypes.PublicNestedTypes)] public MemberInfo[] GetMembers() => GetMembers(Type.DefaultLookup); + /// + /// Searches for the on the current that matches the specified . + /// + /// + /// The to find on the current . + /// + /// An object representing the member on the current that matches the specified member. + /// This method can be used to find a constructed generic member given a member from a generic type definition. + /// is . + /// does not match a member on the current . + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2085:UnrecognizedReflectionPattern", + Justification = "This is finding the MemberInfo with the same MetadataToken as specified MemberInfo. If the specified MemberInfo " + + "exists and wasn't trimmed, then the current Type's MemberInfo couldn't have been trimmed.")] + public virtual MemberInfo GetMemberWithSameMetadataDefinitionAs(MemberInfo member) + { + if (member is null) throw new ArgumentNullException(nameof(member)); + + const BindingFlags all = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; + foreach (MemberInfo myMemberInfo in GetMembers(all)) + { + if (myMemberInfo.HasSameMetadataDefinitionAs(member)) + { + return myMemberInfo; + } + } + + throw CreateGetMemberWithSameMetadataDefinitionAsNotFoundException(member); + } + + private protected static ArgumentException CreateGetMemberWithSameMetadataDefinitionAsNotFoundException(MemberInfo member) => + new ArgumentException(SR.Format(SR.Arg_MemberInfoNotFound, member.Name), nameof(member)); + [DynamicallyAccessedMembers(GetAllMembers)] public abstract MemberInfo[] GetMembers(BindingFlags bindingAttr); diff --git a/src/libraries/System.Reflection/tests/TypeInfoTests.cs b/src/libraries/System.Reflection/tests/TypeInfoTests.cs index 4e3603c..f3b9240 100644 --- a/src/libraries/System.Reflection/tests/TypeInfoTests.cs +++ b/src/libraries/System.Reflection/tests/TypeInfoTests.cs @@ -5,6 +5,8 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Xunit; @@ -1565,6 +1567,70 @@ namespace System.Reflection.Tests Assert.Equal(expected, type.GetTypeInfo().IsSZArray); } + public static IEnumerable GetMemberWithSameMetadataDefinitionAsData() + { + yield return new object[] { typeof(TI_GenericTypeWithAllMembers<>), typeof(TI_GenericTypeWithAllMembers), true }; + yield return new object[] { typeof(TI_GenericTypeWithAllMembers<>), typeof(TI_GenericTypeWithAllMembers), true}; + + yield return new object[] { typeof(TI_GenericTypeWithAllMembers<>), typeof(TI_TypeDerivedFromGenericTypeWithAllMembers), false }; + yield return new object[] { typeof(TI_GenericTypeWithAllMembers<>), typeof(TI_TypeDerivedFromGenericTypeWithAllMembers), false }; + + yield return new object[] { typeof(TI_GenericTypeWithAllMembers<>), typeof(TI_TypeDerivedFromGenericTypeWithAllMembersClosed), false }; + + static TypeInfo GetTypeDelegator(Type t) => new TypeDelegator(t); + yield return new object[] { GetTypeDelegator(typeof(TI_GenericTypeWithAllMembers<>)), typeof(TI_GenericTypeWithAllMembers), true }; + yield return new object[] { typeof(TI_GenericTypeWithAllMembers<>), GetTypeDelegator(typeof(TI_GenericTypeWithAllMembers)), true }; + yield return new object[] { GetTypeDelegator(typeof(TI_GenericTypeWithAllMembers<>)), GetTypeDelegator(typeof(TI_GenericTypeWithAllMembers)), true }; + + if (RuntimeFeature.IsDynamicCodeSupported) + { + (Type generatedType1, Type generatedType2) = CreateGeneratedTypes(); + + yield return new object[] { generatedType1, generatedType2, true }; + yield return new object[] { generatedType2, generatedType1, true }; + } + } + + private static (Type, Type) CreateGeneratedTypes() + { + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("GetMemberWithSameMetadataDefinitionAsGeneratedAssembly"), AssemblyBuilderAccess.Run); + ModuleBuilder module = assembly.DefineDynamicModule("GetMemberWithSameMetadataDefinitionAsGeneratedModule"); + TypeBuilder genericType = module.DefineType("GenericGeneratedType"); + genericType.DefineGenericParameters("T0"); + genericType.DefineField("_int", typeof(int), FieldAttributes.Private); + genericType.DefineProperty("Prop", PropertyAttributes.None, typeof(string), null); + + Type builtGenericType = genericType.CreateType(); + Type closedType = builtGenericType.MakeGenericType(typeof(int)); + + return (genericType, closedType); + } + + [Theory, MemberData(nameof(GetMemberWithSameMetadataDefinitionAsData))] + public void GetMemberWithSameMetadataDefinitionAs(Type openGenericType, Type closedGenericType, bool checkDeclaringType) + { + BindingFlags all = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly; + foreach (MemberInfo openGenericMember in openGenericType.GetMembers(all)) + { + MemberInfo closedGenericMember = closedGenericType.GetMemberWithSameMetadataDefinitionAs(openGenericMember); + Assert.True(closedGenericMember != null, $"'{openGenericMember.Name}' was not found"); + Assert.True(closedGenericMember.HasSameMetadataDefinitionAs(openGenericMember)); + Assert.Equal(closedGenericMember.Name, openGenericMember.Name); + if (checkDeclaringType && openGenericMember is not Type) + { + Assert.True(closedGenericMember.DeclaringType.Equals(closedGenericType), $"'{closedGenericMember.Name}' doesn't have the right DeclaringType"); + } + } + + MemberInfo toString = closedGenericType.GetMemberWithSameMetadataDefinitionAs(typeof(object).GetMethod("ToString")); + Assert.NotNull(toString); + Assert.IsAssignableFrom(toString); + Assert.Equal("ToString", toString.Name); + + Assert.Throws(() => openGenericType.GetMemberWithSameMetadataDefinitionAs(null)); + Assert.Throws(() => openGenericType.GetMemberWithSameMetadataDefinitionAs(typeof(string).GetMethod("get_Length"))); + } + #pragma warning disable 0067, 0169 public static class ClassWithStaticConstructor { @@ -1815,6 +1881,54 @@ namespace System.Reflection.Tests public abstract class AbstractSubClass : AbstractBaseClass { } public class AbstractSubSubClass : AbstractSubClass { } } + + public class TI_GenericTypeWithAllMembers + { + private static event EventHandler PrivateStaticEvent; + private static T PrivateStaticField; + private static T PrivateStaticProperty { get; set; } + private static T PrivateStaticMethod(T t) => default; + private static T PrivateStaticMethod(T t, T t2) => default; + + public static event EventHandler PublicStaticEvent; + public static T PublicStaticField; + public static T PublicStaticProperty { get; set; } + public static T PublicStaticMethod(T t) => default; + public static T PublicStaticMethod(T t, T t2) => default; + + static TI_GenericTypeWithAllMembers() { } + + public TI_GenericTypeWithAllMembers(T t) { } + private TI_GenericTypeWithAllMembers() { } + + public event EventHandler PublicInstanceEvent; + public T PublicInstanceField; + public T PublicInstanceProperty { get; set; } + public T PublicInstanceMethod(T t) => default; + public T PublicInstanceMethod(T t, T t2) => default; + + private event EventHandler PrivateInstanceEvent; + private T PrivateInstanceField; + private T PrivateInstanceProperty { get; set; } + private T PrivateInstanceMethod(T t) => default; + private T PrivateInstanceMethod(T t1, T t2) => default; + + public class Nested + { + public T NestedField; + } + } + + public class TI_TypeDerivedFromGenericTypeWithAllMembers : TI_GenericTypeWithAllMembers + { + public TI_TypeDerivedFromGenericTypeWithAllMembers(T t) : base(t) { } + } + + public class TI_TypeDerivedFromGenericTypeWithAllMembersClosed : TI_TypeDerivedFromGenericTypeWithAllMembers + { + public TI_TypeDerivedFromGenericTypeWithAllMembersClosed(int t) : base(t) { } + } + #pragma warning restore 0067, 0169 public class OutsideTypeInfoTests diff --git a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/ObjectManager.cs b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/ObjectManager.cs index d9eba92..9398d93 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/ObjectManager.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/ObjectManager.cs @@ -16,6 +16,8 @@ namespace System.Runtime.Serialization private const string ObjectManagerUnreferencedCodeMessage = "ObjectManager is not trim compatible because the Type of objects being managed cannot be statically discovered."; + private static readonly FieldInfo s_nullableValueField = typeof(Nullable<>).GetField("value", BindingFlags.NonPublic | BindingFlags.Instance)!; + private DeserializationEventHandler? _onDeserializationHandler; private SerializationEventHandler? _onDeserializedHandler; @@ -383,14 +385,11 @@ namespace System.Runtime.Serialization return true; } - [DynamicDependency("value", typeof(Nullable<>))] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", - Justification = "The Nullable.value field will be preserved by the DynamicDependency.")] private static FieldInfo? GetNullableValueField(Type type) { if (Nullable.GetUnderlyingType(type) != null) { - return type.GetField("value", BindingFlags.NonPublic | BindingFlags.Instance)!; + return (FieldInfo)type.GetMemberWithSameMetadataDefinitionAs(s_nullableValueField); } return null; diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index a251145..736ba37 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -4485,6 +4485,7 @@ namespace System public virtual System.Reflection.MemberInfo[] GetMember(string name, System.Reflection.BindingFlags bindingAttr) { throw null; } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicNestedTypes | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicProperties | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicNestedTypes | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] public virtual System.Reflection.MemberInfo[] GetMember(string name, System.Reflection.MemberTypes type, System.Reflection.BindingFlags bindingAttr) { throw null; } + public virtual System.Reflection.MemberInfo GetMemberWithSameMetadataDefinitionAs(System.Reflection.MemberInfo member) { throw null; } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicNestedTypes | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] public System.Reflection.MemberInfo[] GetMembers() { throw null; } [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicNestedTypes | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicProperties | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicEvents | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicFields | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicMethods | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicNestedTypes | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicProperties)] diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs index 2c7fbda..e92011c 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeEventInfo.cs @@ -82,14 +82,6 @@ namespace System.Reflection return (RuntimeType)DeclaringType; } - private RuntimeType ReflectedTypeInternal - { - get - { - return (RuntimeType)ReflectedType; - } - } - internal RuntimeModule GetRuntimeModule() { return GetDeclaringTypeInternal().GetRuntimeModule(); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs index b4a9c2f..15649ac 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeFieldInfo.cs @@ -63,14 +63,6 @@ namespace System.Reflection return (RuntimeType)DeclaringType!; } - private RuntimeType ReflectedTypeInternal - { - get - { - return (RuntimeType)ReflectedType; - } - } - internal RuntimeModule GetRuntimeModule() { return GetDeclaringTypeInternal().GetRuntimeModule(); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs index aba9b94..362b999 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs @@ -157,14 +157,6 @@ namespace System.Reflection } } - private RuntimeType? ReflectedTypeInternal - { - get - { - return (RuntimeType?)ReflectedType; - } - } - private string FormatNameAndSig() { // Serialization uses ToString to resolve MethodInfo overloads. @@ -788,14 +780,6 @@ namespace System.Reflection return RuntimeTypeHandle.GetModule((RuntimeType)DeclaringType); } - private RuntimeType? ReflectedTypeInternal - { - get - { - return (RuntimeType?)ReflectedType; - } - } - public override MethodImplAttributes GetMethodImplementationFlags() { return MonoMethodInfo.GetMethodImplementationFlags(mhandle); diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs index 9e05a18..20defaa 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimePropertyInfo.cs @@ -131,14 +131,6 @@ namespace System.Reflection return (RuntimeType)DeclaringType; } - private RuntimeType ReflectedTypeInternal - { - get - { - return (RuntimeType)ReflectedType; - } - } - internal RuntimeModule GetRuntimeModule() { return GetDeclaringTypeInternal().GetRuntimeModule(); diff --git a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs index 7b9c50c..241fee8 100644 --- a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Collections.Generic; using System.Runtime.Serialization; using System.Runtime.CompilerServices; -using System.Diagnostics.Contracts; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Diagnostics; @@ -38,14 +37,6 @@ namespace System FormatFullInst } - internal enum TypeNameKind - { - Name, - ToString, - SerializationName, - FullName, - } - internal partial class RuntimeType { #region Definitions @@ -1127,8 +1118,135 @@ namespace System return compressMembers; } - #endregion + public override MemberInfo GetMemberWithSameMetadataDefinitionAs(MemberInfo member) + { + if (member is null) throw new ArgumentNullException(nameof(member)); + + RuntimeType? runtimeType = this; + while (runtimeType != null) + { + MemberInfo? result = member.MemberType switch + { + MemberTypes.Method => GetMethodWithSameMetadataDefinitionAs(runtimeType, member), + MemberTypes.Constructor => GetConstructorWithSameMetadataDefinitionAs(runtimeType, member), + MemberTypes.Property => GetPropertyWithSameMetadataDefinitionAs(runtimeType, member), + MemberTypes.Field => GetFieldWithSameMetadataDefinitionAs(runtimeType, member), + MemberTypes.Event => GetEventWithSameMetadataDefinitionAs(runtimeType, member), + MemberTypes.NestedType => GetNestedTypeWithSameMetadataDefinitionAs(runtimeType, member), + _ => null + }; + + if (result != null) + { + return result; + } + + runtimeType = runtimeType.GetBaseType(); + } + + throw CreateGetMemberWithSameMetadataDefinitionAsNotFoundException(member); + } + + private const BindingFlags GetMemberWithSameMetadataDefinitionAsBindingFlags = + BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + + private static MemberInfo? GetMethodWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo methodInfo) + { + ListBuilder methods = runtimeType.GetMethodCandidates(methodInfo.Name, GetMemberWithSameMetadataDefinitionAsBindingFlags, CallingConventions.Any, null, -1, allowPrefixLookup: false); + + for (int i = 0; i < methods.Count; i++) + { + MethodInfo candidate = methods[i]; + if (candidate.HasSameMetadataDefinitionAs(methodInfo)) + { + return candidate; + } + } + + return null; + } + + private static MemberInfo? GetConstructorWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo constructorInfo) + { + ListBuilder ctors = runtimeType.GetConstructorCandidates(null, GetMemberWithSameMetadataDefinitionAsBindingFlags, CallingConventions.Any, null, allowPrefixLookup: false); + + for (int i = 0; i < ctors.Count; i++) + { + ConstructorInfo candidate = ctors[i]; + if (candidate.HasSameMetadataDefinitionAs(constructorInfo)) + { + return candidate; + } + } + + return null; + } + + private static MemberInfo? GetPropertyWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo propertyInfo) + { + ListBuilder properties = runtimeType.GetPropertyCandidates(propertyInfo.Name, GetMemberWithSameMetadataDefinitionAsBindingFlags, null, allowPrefixLookup: false); + + for (int i = 0; i < properties.Count; i++) + { + PropertyInfo candidate = properties[i]; + if (candidate.HasSameMetadataDefinitionAs(propertyInfo)) + { + return candidate; + } + } + + return null; + } + + private static MemberInfo? GetFieldWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo fieldInfo) + { + ListBuilder fields = runtimeType.GetFieldCandidates(fieldInfo.Name, GetMemberWithSameMetadataDefinitionAsBindingFlags, allowPrefixLookup: false); + + for (int i = 0; i < fields.Count; i++) + { + FieldInfo candidate = fields[i]; + if (candidate.HasSameMetadataDefinitionAs(fieldInfo)) + { + return candidate; + } + } + + return null; + } + + private static MemberInfo? GetEventWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo eventInfo) + { + ListBuilder events = runtimeType.GetEventCandidates(null, GetMemberWithSameMetadataDefinitionAsBindingFlags, allowPrefixLookup: false); + + for (int i = 0; i < events.Count; i++) + { + EventInfo candidate = events[i]; + if (candidate.HasSameMetadataDefinitionAs(eventInfo)) + { + return candidate; + } + } + + return null; + } + + private static MemberInfo? GetNestedTypeWithSameMetadataDefinitionAs(RuntimeType runtimeType, MemberInfo nestedType) + { + ListBuilder nestedTypes = runtimeType.GetNestedTypeCandidates(nestedType.Name, GetMemberWithSameMetadataDefinitionAsBindingFlags, allowPrefixLookup: false); + + for (int i = 0; i < nestedTypes.Count; i++) + { + Type candidate = nestedTypes[i]; + if (candidate.HasSameMetadataDefinitionAs(nestedType)) + { + return candidate; + } + } + + return null; + } + #endregion #region Hierarchy -- 2.7.4