From b1e17feacfcb4d63bd66a53481a9e850f79c4f8d Mon Sep 17 00:00:00 2001 From: Atsushi Kanamori Date: Mon, 22 May 2017 10:50:49 -0700 Subject: [PATCH] Implement HasSameMetadataDefinitionAs() on CoreRT This api was approved here: https://github.com/dotnet/corefx/issues/5884 and is a necessary step to fixing the System.Dynamic.Runtime.Tests failure: https://github.com/dotnet/corefx/issues/19895 which is caused by Microsoft.CSharp trying to do the impossible and emulate this api without GetMetadataToken() support. This emulates the behavior of the CoreCLR version (which simply compares MetadataTokens and Modules.) The CoreCLR version of this is currently a PR https://github.com/dotnet/coreclr/pull/11774 -- Copied from CoreCLR commit text--- This results in the following behavior for these corner cases. With the possible exception of #3, I think they are tolerable enough to accept and codify: 1. "other" implemented by an entirely different Reflection provider than "this". Behavior: returns false without invoking any methods on the "other" Member. To change it to throw an ArgumentException would mean extra cast checks against the 6 possible Runtime types (or having said RuntimeTypes implement a sentinel interface.) Given that HasSameMetadataDefinitionAs() is a "looser cousin of Equals()" and "Equals()" doesn't throw for objects from a different universe, this seems reasonable. 2. Arrays, ByRefs, Pointers and Types from GetTypeFromCLSID() Behavior: Arrays, ByRefs, Pointers all return token 0x0600000 and so they'll return "true" wrt to each other (provided both types are implemented by the same provider.) CLSID types all return the typedef of __ComObject so they'll return "true" wrt to each other. The constructor exposed by CLSID types all return the typedef of some constructor on __ComObject so they'll return "true" wrt to each other. I do not think these are interesting cases that merit special handling. These types will never appear in an enumeration of the members of a type. (The fact that Reflection surfaces them in objects that are assignable to MemberInfo is a structural flaw in Reflection's object model.) 3. Synthesized constructors and methods on array types. Behavior: These methods all return 0x06000000 as a token so the constructors will all compare true wrt each other, and likewise with the methods. This is a bit crummy though it's not clear what the "right" policy should look like. I could be persuaded to throw NotSupported for these, to leave the possibility open for a better story later. On the other hand, I wouldn't demand it either. --- .../shared/System/Reflection/MemberInfo.cs | 2 ++ .../src/System.Private.Reflection.Core.csproj | 1 + .../EcmaFormat/EcmaFormatRuntimeEventInfo.cs | 15 +++++++++++++ .../NativeFormatRuntimeEventInfo.cs | 17 ++++++++++++++ .../Runtime/EventInfos/RuntimeEventInfo.cs | 2 ++ .../EcmaFormat/EcmaFormatRuntimeFieldInfo.cs | 15 +++++++++++++ .../NativeFormatRuntimeFieldInfo.cs | 17 ++++++++++++++ .../Runtime/FieldInfos/RuntimeFieldInfo.cs | 2 ++ ...ntimeMemberInfoWithNoMetadataDefinition.cs | 16 ++++++++++++++ .../EcmaFormat/EcmaFormatMethodCommon.cs | 9 ++++++++ .../MethodInfos/IRuntimeMethodCommon.cs | 2 ++ .../NativeFormat/NativeFormatMethodCommon.cs | 11 ++++++++++ .../RuntimeClsIdNullaryConstructorInfo.cs | 9 ++++++++ .../RuntimeConstructedGenericMethodInfo.cs | 5 +++++ .../MethodInfos/RuntimeConstructorInfo.cs | 2 ++ .../MethodInfos/RuntimeDummyMethodInfo.cs | 1 + .../Runtime/MethodInfos/RuntimeMethodInfo.cs | 2 ++ .../MethodInfos/RuntimeNamedMethodInfo.cs | 17 ++++++++++++++ .../RuntimePlainConstructorInfo.cs | 11 ++++++++++ .../RuntimeSyntheticConstructorInfo.cs | 13 ++++++++++- .../MethodInfos/RuntimeSyntheticMethodInfo.cs | 13 ++++++++++- .../EcmaFormatRuntimePropertyInfo.cs | 15 +++++++++++++ .../NativeFormatRuntimePropertyInfo.cs | 18 +++++++++++++++ .../PropertyInfos/RuntimePropertyInfo.cs | 2 ++ ...RuntimeGenericParameterTypeInfoForTypes.cs | 1 + .../Runtime/TypeInfos/RuntimeArrayTypeInfo.cs | 22 ++++++++++++++++++- .../TypeInfos/RuntimeBlockedTypeInfo.cs | 4 ++++ .../Runtime/TypeInfos/RuntimeClsIdTypeInfo.cs | 9 ++++++++ .../RuntimeConstructedGenericTypeInfo.cs | 5 +++++ .../RuntimeGenericParameterTypeInfo.cs | 13 +++++++++++ .../TypeInfos/RuntimeHasElementTypeInfo.cs | 11 +++++++++- .../Runtime/TypeInfos/RuntimeNamedTypeInfo.cs | 4 ++++ .../RuntimeNoMetadataNamedTypeInfo.cs | 4 ++++ .../RuntimeTypeDefinitionTypeInfo.cs | 19 ++++++++++++++++ .../Runtime/TypeInfos/RuntimeTypeInfo.cs | 2 ++ 35 files changed, 307 insertions(+), 4 deletions(-) create mode 100644 src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/IRuntimeMemberInfoWithNoMetadataDefinition.cs diff --git a/src/System.Private.CoreLib/shared/System/Reflection/MemberInfo.cs b/src/System.Private.CoreLib/shared/System/Reflection/MemberInfo.cs index 1275cc15a05..d8a7458632b 100644 --- a/src/System.Private.CoreLib/shared/System/Reflection/MemberInfo.cs +++ b/src/System.Private.CoreLib/shared/System/Reflection/MemberInfo.cs @@ -30,6 +30,8 @@ public virtual Module Module } } + public virtual bool HasSameMetadataDefinitionAs(MemberInfo other) { throw NotImplemented.ByDesign; } + public abstract bool IsDefined(Type attributeType, bool inherit); public abstract object[] GetCustomAttributes(bool inherit); public abstract object[] GetCustomAttributes(Type attributeType, bool inherit); diff --git a/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj b/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj index ec4d724c524..9ce0ef1c4e6 100644 --- a/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj +++ b/src/System.Private.Reflection.Core/src/System.Private.Reflection.Core.csproj @@ -111,6 +111,7 @@ + diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/EcmaFormat/EcmaFormatRuntimeEventInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/EcmaFormat/EcmaFormatRuntimeEventInfo.cs index d61a80380c1..87820be0acb 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/EcmaFormat/EcmaFormatRuntimeEventInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/EcmaFormat/EcmaFormatRuntimeEventInfo.cs @@ -113,6 +113,21 @@ public sealed override IEnumerable CustomAttributes } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + EcmaFormatRuntimeEventInfo otherEvent = other as EcmaFormatRuntimeEventInfo; + if (otherEvent == null) + return false; + if (!(_reader == otherEvent._reader)) + return false; + if (!(_eventHandle.Equals(otherEvent._eventHandle))) + return false; + return true; + } + public sealed override bool Equals(Object obj) { EcmaFormatRuntimeEventInfo other = obj as EcmaFormatRuntimeEventInfo; diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs index 032f213e713..f738c4e3ccd 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/NativeFormat/NativeFormatRuntimeEventInfo.cs @@ -111,6 +111,23 @@ public sealed override IEnumerable CustomAttributes } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + NativeFormatRuntimeEventInfo otherEvent = other as NativeFormatRuntimeEventInfo; + if (otherEvent == null) + return false; + if (!(_reader == otherEvent._reader)) + return false; + if (!(_eventHandle.Equals(otherEvent._eventHandle))) + return false; + if (!(_definingTypeInfo.Equals(otherEvent._definingTypeInfo))) + return false; + return true; + } + public sealed override bool Equals(Object obj) { NativeFormatRuntimeEventInfo other = obj as NativeFormatRuntimeEventInfo; diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs index fea20265791..5ba18c687a3 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/EventInfos/RuntimeEventInfo.cs @@ -78,6 +78,8 @@ public sealed override MethodInfo[] GetOtherMethods(bool nonPublic) throw new PlatformNotSupportedException(); } + public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other); + public sealed override Module Module { get diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/EcmaFormat/EcmaFormatRuntimeFieldInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/EcmaFormat/EcmaFormatRuntimeFieldInfo.cs index a058fab710f..3da17059cb9 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/EcmaFormat/EcmaFormatRuntimeFieldInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/EcmaFormat/EcmaFormatRuntimeFieldInfo.cs @@ -131,6 +131,21 @@ public sealed override String ToString() } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + EcmaFormatRuntimeFieldInfo otherField = other as EcmaFormatRuntimeFieldInfo; + if (otherField == null) + return false; + if (!(_reader == otherField._reader)) + return false; + if (!(_fieldHandle.Equals(otherField._fieldHandle))) + return false; + return true; + } + public sealed override bool Equals(Object obj) { EcmaFormatRuntimeFieldInfo other = obj as EcmaFormatRuntimeFieldInfo; diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs index 97d9d174700..ac03d6b99f4 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/NativeFormat/NativeFormatRuntimeFieldInfo.cs @@ -112,6 +112,23 @@ public sealed override String ToString() return (new QTypeDefRefOrSpec(_reader, typeHandle).FormatTypeName(typeContext)) + " " + this.Name; } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + NativeFormatRuntimeFieldInfo otherField = other as NativeFormatRuntimeFieldInfo; + if (otherField == null) + return false; + if (!(_reader == otherField._reader)) + return false; + if (!(_fieldHandle.Equals(otherField._fieldHandle))) + return false; + if (!(_definingTypeInfo.Equals(otherField._definingTypeInfo))) + return false; + return true; + } + public sealed override bool Equals(Object obj) { NativeFormatRuntimeFieldInfo other = obj as NativeFormatRuntimeFieldInfo; diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs index 77fa3abf369..3890911371d 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/FieldInfos/RuntimeFieldInfo.cs @@ -104,6 +104,8 @@ public sealed override object GetValueDirect(TypedReference obj) return fieldAccessor.GetFieldDirect(obj); } + public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other); + public sealed override Module Module { get diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/IRuntimeMemberInfoWithNoMetadataDefinition.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/IRuntimeMemberInfoWithNoMetadataDefinition.cs new file mode 100644 index 00000000000..4a5aa40d888 --- /dev/null +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/IRuntimeMemberInfoWithNoMetadataDefinition.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Reflection.Runtime.General +{ + // This interface's presence on a MemberInfo testates that + // + // 1. The MemberInfo implemented by Reflection.Core + // 2. Is to be lumped into the "no metadata token" group for the purposes + // of the HasSameMetadataDefinitionAs() api. + // + internal interface IRuntimeMemberInfoWithNoMetadataDefinition + { + } +} diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/EcmaFormat/EcmaFormatMethodCommon.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/EcmaFormat/EcmaFormatMethodCommon.cs index b1d9a2d972f..8249cc3c78c 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/EcmaFormat/EcmaFormatMethodCommon.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/EcmaFormat/EcmaFormatMethodCommon.cs @@ -312,6 +312,15 @@ public MethodDefinitionHandle MethodHandle } } + public bool HasSameMetadataDefinitionAs(EcmaFormatMethodCommon other) + { + if (!(_reader == other._reader)) + return false; + if (!(_methodHandle.Equals(other._methodHandle))) + return false; + return true; + } + public override bool Equals(Object obj) { if (!(obj is EcmaFormatMethodCommon)) diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/IRuntimeMethodCommon.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/IRuntimeMethodCommon.cs index 01d872c67ff..cba2855425c 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/IRuntimeMethodCommon.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/IRuntimeMethodCommon.cs @@ -50,6 +50,8 @@ internal interface IRuntimeMethodCommon where TRuntimeMeth bool IsGenericMethodDefinition { get; } + bool HasSameMetadataDefinitionAs(TRuntimeMethodCommon other); + TRuntimeMethodCommon RuntimeMethodCommonOfUninstantiatedMethod { get; } RuntimeTypeInfo[] GetGenericTypeParametersWithSpecifiedOwningMethod(RuntimeNamedMethodInfo owningMethod); diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/NativeFormat/NativeFormatMethodCommon.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/NativeFormat/NativeFormatMethodCommon.cs index 325eb1f7888..e44cf627bd8 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/NativeFormat/NativeFormatMethodCommon.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/NativeFormat/NativeFormatMethodCommon.cs @@ -311,6 +311,17 @@ public MethodHandle MethodHandle } } + public bool HasSameMetadataDefinitionAs(NativeFormatMethodCommon other) + { + if (!(_reader == other._reader)) + return false; + if (!(_methodHandle.Equals(other._methodHandle))) + return false; + if (!(_definingTypeInfo.Equals(other._definingTypeInfo))) + return false; + return true; + } + public override bool Equals(Object obj) { if (!(obj is NativeFormatMethodCommon)) diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeClsIdNullaryConstructorInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeClsIdNullaryConstructorInfo.cs index 9fd1adf7711..e9f59b1febb 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeClsIdNullaryConstructorInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeClsIdNullaryConstructorInfo.cs @@ -30,6 +30,15 @@ private RuntimeCLSIDNullaryConstructorInfo(RuntimeCLSIDTypeInfo declaringType) public sealed override IEnumerable CustomAttributes => Empty.Enumerable; public sealed override Type DeclaringType => _declaringType; + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // This logic is written to match CoreCLR's behavior. + return other is RuntimeCLSIDNullaryConstructorInfo; + } + public sealed override bool Equals(object obj) { RuntimeCLSIDNullaryConstructorInfo other = obj as RuntimeCLSIDNullaryConstructorInfo; diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs index ec7bab4a010..cbab726c4ae 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructedGenericMethodInfo.cs @@ -49,6 +49,11 @@ public sealed override IEnumerable CustomAttributes } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + return _genericMethodDefinition.HasSameMetadataDefinitionAs(other); + } + public sealed override bool Equals(Object obj) { RuntimeConstructedGenericMethodInfo other = obj as RuntimeConstructedGenericMethodInfo; diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructorInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructorInfo.cs index 25c47e17a18..a54967600d3 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructorInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeConstructorInfo.cs @@ -79,6 +79,8 @@ public sealed override ParameterInfo[] GetParametersNoCopy() return RuntimeParameters; } + public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other); + public abstract override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture); [DebuggerGuidedStepThrough] diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeDummyMethodInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeDummyMethodInfo.cs index bb67ebe8bb2..1423d21f7ea 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeDummyMethodInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeDummyMethodInfo.cs @@ -29,6 +29,7 @@ private RuntimeDummyMethodInfo() { } public sealed override bool IsConstructedGenericMethod { get { throw NotImplemented.ByDesign; } } public sealed override bool IsGenericMethod { get { throw NotImplemented.ByDesign; } } public sealed override bool IsGenericMethodDefinition { get { throw NotImplemented.ByDesign; } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) { throw NotImplemented.ByDesign; } public sealed override MethodImplAttributes MethodImplementationFlags { get { throw NotImplemented.ByDesign; } } public sealed override Module Module { get { throw NotImplemented.ByDesign; } } public sealed override MethodBase MetadataDefinitionMethod { get { throw NotImplemented.ByDesign; } } diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodInfo.cs index 1b31a5cd59a..bf5ef0cd4e5 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeMethodInfo.cs @@ -185,6 +185,8 @@ public sealed override ParameterInfo[] GetParametersNoCopy() return RuntimeParameters; } + public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other); + [DebuggerGuidedStepThroughAttribute] public sealed override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) { diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs index 3cbed32156d..03beae11c2a 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs @@ -189,6 +189,23 @@ public sealed override String ToString() return ComputeToString(this); } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // Do not rewrite as a call to IsConstructedGenericMethod - we haven't yet established that "other" is a runtime-implemented member yet! + RuntimeConstructedGenericMethodInfo otherConstructedGenericMethod = other as RuntimeConstructedGenericMethodInfo; + if (otherConstructedGenericMethod != null) + other = otherConstructedGenericMethod.GetGenericMethodDefinition(); + + RuntimeNamedMethodInfo otherMethod = other as RuntimeNamedMethodInfo; + if (otherMethod == null) + return false; + + return _common.HasSameMetadataDefinitionAs(otherMethod._common); + } + public sealed override bool Equals(Object obj) { RuntimeNamedMethodInfo other = obj as RuntimeNamedMethodInfo; diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimePlainConstructorInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimePlainConstructorInfo.cs index 37eeb57a852..0dbb40e002d 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimePlainConstructorInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimePlainConstructorInfo.cs @@ -145,6 +145,17 @@ public sealed override int MetadataToken } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + RuntimePlainConstructorInfo otherConstructor = other as RuntimePlainConstructorInfo; + if (otherConstructor == null) + return false; + return _common.HasSameMetadataDefinitionAs(otherConstructor._common); + } + public sealed override bool Equals(Object obj) { RuntimePlainConstructorInfo other = obj as RuntimePlainConstructorInfo; diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticConstructorInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticConstructorInfo.cs index 8cdced0cc75..202a3447f57 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticConstructorInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticConstructorInfo.cs @@ -18,10 +18,12 @@ namespace System.Reflection.Runtime.MethodInfos // // The runtime's implementation of constructors exposed on array types. // - internal sealed partial class RuntimeSyntheticConstructorInfo : RuntimeConstructorInfo + internal sealed partial class RuntimeSyntheticConstructorInfo : RuntimeConstructorInfo, IRuntimeMemberInfoWithNoMetadataDefinition { private RuntimeSyntheticConstructorInfo(SyntheticMethodId syntheticMethodId, RuntimeTypeInfo declaringType, RuntimeTypeInfo[] runtimeParameterTypes, InvokerOptions options, Func invoker) { + Debug.Assert(declaringType.IsArray); + _syntheticMethodId = syntheticMethodId; _declaringType = declaringType; _options = options; @@ -104,6 +106,15 @@ public sealed override String Name } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // This logic is written to match CoreCLR's behavior. + return other is ConstructorInfo && other is IRuntimeMemberInfoWithNoMetadataDefinition; + } + public sealed override bool Equals(object obj) { RuntimeSyntheticConstructorInfo other = obj as RuntimeSyntheticConstructorInfo; diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs index 64144d6a5fe..8b0c8ad245c 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/MethodInfos/RuntimeSyntheticMethodInfo.cs @@ -17,10 +17,12 @@ namespace System.Reflection.Runtime.MethodInfos // // These methods implement the Get/Set methods on array types. // - internal sealed partial class RuntimeSyntheticMethodInfo : RuntimeMethodInfo + internal sealed partial class RuntimeSyntheticMethodInfo : RuntimeMethodInfo, IRuntimeMemberInfoWithNoMetadataDefinition { private RuntimeSyntheticMethodInfo(SyntheticMethodId syntheticMethodId, String name, RuntimeTypeInfo declaringType, RuntimeTypeInfo[] parameterTypes, RuntimeTypeInfo returnType, InvokerOptions options, Func invoker) { + Debug.Assert(declaringType.IsArray); + _syntheticMethodId = syntheticMethodId; _name = name; _declaringType = declaringType; @@ -54,6 +56,15 @@ public sealed override IEnumerable CustomAttributes } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // This logic is written to match CoreCLR's behavior. + return other is MethodInfo && other is IRuntimeMemberInfoWithNoMetadataDefinition; + } + public sealed override bool Equals(Object obj) { RuntimeSyntheticMethodInfo other = obj as RuntimeSyntheticMethodInfo; diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/EcmaFormat/EcmaFormatRuntimePropertyInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/EcmaFormat/EcmaFormatRuntimePropertyInfo.cs index 26874e47f0a..1d57210f8a4 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/EcmaFormat/EcmaFormatRuntimePropertyInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/EcmaFormat/EcmaFormatRuntimePropertyInfo.cs @@ -83,6 +83,21 @@ public sealed override IEnumerable CustomAttributes } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + EcmaFormatRuntimePropertyInfo otherProperty = other as EcmaFormatRuntimePropertyInfo; + if (otherProperty == null) + return false; + if (!(_reader == otherProperty._reader)) + return false; + if (!(_propertyHandle.Equals(otherProperty._propertyHandle))) + return false; + return true; + } + public sealed override bool Equals(Object obj) { EcmaFormatRuntimePropertyInfo other = obj as EcmaFormatRuntimePropertyInfo; diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs index 4d3eb9b70e9..3cd353ef4f4 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/NativeFormat/NativeFormatRuntimePropertyInfo.cs @@ -85,6 +85,23 @@ public sealed override IEnumerable CustomAttributes } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + NativeFormatRuntimePropertyInfo otherProperty = other as NativeFormatRuntimePropertyInfo; + if (otherProperty == null) + return false; + if (!(_reader == otherProperty._reader)) + return false; + if (!(_propertyHandle.Equals(otherProperty._propertyHandle))) + return false; + if (!(_definingTypeInfo.Equals(otherProperty._definingTypeInfo))) + return false; + return true; + } + public sealed override bool Equals(Object obj) { NativeFormatRuntimePropertyInfo other = obj as NativeFormatRuntimePropertyInfo; @@ -126,6 +143,7 @@ public sealed override Object GetConstantValue() return defaultValue; } + public sealed override int MetadataToken { get diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs index 6f22d862c54..62e2d22dc6c 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/PropertyInfos/RuntimePropertyInfo.cs @@ -163,6 +163,8 @@ public sealed override object GetValue(object obj, BindingFlags invokeAttr, Bind return _lazyGetterInvoker.Invoke(obj, index, binder, invokeAttr, culture); } + public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other); + public sealed override Module Module { get diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.cs index d8c6a3af80d..a97a6a8aba6 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/NativeFormat/NativeFormatRuntimeGenericParameterTypeInfoForTypes.cs @@ -20,6 +20,7 @@ internal sealed partial class NativeFormatRuntimeGenericParameterTypeInfoForType private NativeFormatRuntimeGenericParameterTypeInfoForTypes(MetadataReader reader, GenericParameterHandle genericParameterHandle, RuntimeTypeInfo declaringRuntimeNamedTypeInfo) : base(reader, genericParameterHandle, genericParameterHandle.GetGenericParameter(reader)) { + Debug.Assert(declaringRuntimeNamedTypeInfo.IsTypeDefinition); _declaringRuntimeNamedTypeInfo = declaringRuntimeNamedTypeInfo; } diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs index feb436d87bc..4a57aef2bd2 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeArrayTypeInfo.cs @@ -55,6 +55,16 @@ internal sealed override IEnumerable SyntheticConstructo RuntimeTypeInfo countType = CommonRuntimeTypes.Int32.CastToRuntimeTypeInfo(); { + // + // Expose a constructor that takes n Int32's (one for each dimension) and constructs a zero lower-bounded array. For example, + // + // String[,] + // + // exposes + // + // .ctor(int32, int32) + // + RuntimeTypeInfo[] ctorParameters = new RuntimeTypeInfo[rank]; for (int i = 0; i < rank; i++) ctorParameters[i] = countType; @@ -91,7 +101,7 @@ internal sealed override IEnumerable SyntheticConstructo int parameterCount = 2; RuntimeTypeInfo elementType = this.InternalRuntimeElementType; - while (elementType.IsArray && elementType.GetArrayRank() == 1) + while (elementType.IsSZArray) { RuntimeTypeInfo[] ctorParameters = new RuntimeTypeInfo[parameterCount]; for (int i = 0; i < parameterCount; i++) @@ -119,6 +129,16 @@ internal sealed override IEnumerable SyntheticConstructo if (multiDim) { + // + // Expose a constructor that takes n*2 Int32's (two for each dimension) and constructs a arbitrarily lower-bounded array. For example, + // + // String[,] + // + // exposes + // + // .ctor(int32, int32, int32, int32) + // + RuntimeTypeInfo[] ctorParameters = new RuntimeTypeInfo[rank * 2]; for (int i = 0; i < rank * 2; i++) ctorParameters[i] = countType; diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeBlockedTypeInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeBlockedTypeInfo.cs index 9e826d28e9e..0c0271cc9eb 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeBlockedTypeInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeBlockedTypeInfo.cs @@ -86,6 +86,10 @@ public sealed override Guid GUID } } +#if DEBUG + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => base.HasSameMetadataDefinitionAs(other); +#endif + public sealed override bool IsGenericTypeDefinition { get diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.cs index 0a13c97232f..b09238548fa 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeClsIdTypeInfo.cs @@ -36,6 +36,15 @@ private RuntimeCLSIDTypeInfo(Guid clsid, string server) public sealed override StructLayoutAttribute StructLayoutAttribute => BaseType.StructLayoutAttribute; public sealed override string ToString() => BaseType.ToString(); + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // This logic is written to match CoreCLR's behavior. + return other is RuntimeCLSIDTypeInfo; + } + protected sealed override TypeAttributes GetAttributeFlagsImpl() => TypeAttributes.Public; protected sealed override int InternalGetHashCode() => _key.GetHashCode(); diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.cs index 8fbe1c65403..3f2d8ad36cf 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeConstructedGenericTypeInfo.cs @@ -147,6 +147,11 @@ public sealed override bool ContainsGenericParameters } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + return GenericTypeDefinitionTypeInfo.HasSameMetadataDefinitionAs(other); + } + public sealed override string Namespace { get diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeGenericParameterTypeInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeGenericParameterTypeInfo.cs index 81cea48c78f..fb030e856b1 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeGenericParameterTypeInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeGenericParameterTypeInfo.cs @@ -73,6 +73,19 @@ public sealed override string FullName } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // Unlike most other MemberInfo objects, generic parameter types never get cloned due to containing generic types being instantiated. + // That is, their DeclaringType is always the generic type definition. As a Type, the ReflectedType property is always equal to the DeclaringType. + // + // Because of these conditions, we can safely implement both the method token equivalence and the "is this type from the same implementor" + // check as our regular Equals() method. + return Equals(other); + } + public sealed override int GenericParameterPosition { get diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.cs index f22d2178255..e60ec631390 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeHasElementTypeInfo.cs @@ -18,7 +18,7 @@ namespace System.Reflection.Runtime.TypeInfos // // The runtime's implementation of TypeInfo's for the "HasElement" subclass of types. // - internal abstract partial class RuntimeHasElementTypeInfo : RuntimeTypeInfo, IKeyedItem + internal abstract partial class RuntimeHasElementTypeInfo : RuntimeTypeInfo, IKeyedItem, IRuntimeMemberInfoWithNoMetadataDefinition { protected RuntimeHasElementTypeInfo(UnificationKey key) : base() @@ -96,6 +96,15 @@ public sealed override string FullName } } + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // This logic is written to match CoreCLR's behavior. + return other is Type && other is IRuntimeMemberInfoWithNoMetadataDefinition; + } + public sealed override string Namespace { get diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNamedTypeInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNamedTypeInfo.cs index 73ff2aea91c..da34e97142f 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNamedTypeInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNamedTypeInfo.cs @@ -108,6 +108,10 @@ public sealed override string FullName } } +#if DEBUG + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => base.HasSameMetadataDefinitionAs(other); +#endif + protected abstract void GetPackSizeAndSize(out int packSize, out int size); public sealed override StructLayoutAttribute StructLayoutAttribute diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNoMetadataNamedTypeInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNoMetadataNamedTypeInfo.cs index a9928794e78..42eca1a6da2 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNoMetadataNamedTypeInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNoMetadataNamedTypeInfo.cs @@ -83,6 +83,10 @@ public sealed override bool IsGenericTypeDefinition } } +#if DEBUG + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => base.HasSameMetadataDefinitionAs(other); +#endif + public sealed override string Namespace { get diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeDefinitionTypeInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeDefinitionTypeInfo.cs index edf46d6fbd2..0ab216de226 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeDefinitionTypeInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeDefinitionTypeInfo.cs @@ -19,5 +19,24 @@ internal abstract class RuntimeTypeDefinitionTypeInfo : RuntimeTypeInfo protected sealed override bool IsPointerImpl() => false; public sealed override bool IsConstructedGenericType => false; public sealed override bool IsGenericParameter => false; + + // Left unsealed as RuntimeCLSIDTypeInfo has special behavior and needs to override. + public override bool HasSameMetadataDefinitionAs(MemberInfo other) + { + if (other == null) + throw new ArgumentNullException(nameof(other)); + + // Do not rewrite as a call to IsConstructedGenericType - we haven't yet established that "other" is a runtime-implemented member yet! + RuntimeConstructedGenericTypeInfo otherConstructedGenericType = other as RuntimeConstructedGenericTypeInfo; + if (otherConstructedGenericType != null) + other = otherConstructedGenericType.GetGenericTypeDefinition(); + + // Unlike most other MemberInfo objects, types never get cloned due to containing generic types being instantiated. + // That is, their DeclaringType is always the generic type definition. As a Type, the ReflectedType property is always equal to the DeclaringType. + // + // Because of these conditions, we can safely implement both the method token equivalence and the "is this type from the same implementor" + // check as our regular Equals() method. + return Equals(other); + } } } diff --git a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs index b47744abb66..ec97fc332ea 100644 --- a/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs +++ b/src/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeTypeInfo.cs @@ -229,6 +229,8 @@ public override Guid GUID } } + public abstract override bool HasSameMetadataDefinitionAs(MemberInfo other); + public sealed override IEnumerable ImplementedInterfaces { get