From c194074a82ee35f31b1e55233b43256713b26467 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Fri, 8 Mar 2019 17:36:37 -0800 Subject: [PATCH] Properly override IsEquivalentTo() API in RuntimeType (#23137) * Properly override IsEquivalentTo() API in RuntimeType * Add tests for API validation --- src/System.Private.CoreLib/src/System/RtType.cs | 19 +++++++++++++ .../src/System/RuntimeHandles.cs | 5 ++++ src/vm/ecalllist.h | 1 + src/vm/runtimehandles.cpp | 19 +++++++++++++ src/vm/runtimehandles.h | 2 ++ .../baseservices/typeequivalence/simple/Simple.cs | 31 +++++++++++++++++++++- 6 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/System.Private.CoreLib/src/System/RtType.cs b/src/System.Private.CoreLib/src/System/RtType.cs index 2a71d2e..3d03771 100644 --- a/src/System.Private.CoreLib/src/System/RtType.cs +++ b/src/System.Private.CoreLib/src/System/RtType.cs @@ -3225,6 +3225,25 @@ namespace System return false; } +#if FEATURE_TYPEEQUIVALENCE + // Reflexive, symmetric, transitive. + public override bool IsEquivalentTo(Type other) + { + var otherRtType = other as RuntimeType; + if (otherRtType is null) + { + return false; + } + + if (otherRtType == this) + { + return true; + } + + return RuntimeTypeHandle.IsEquivalentTo(this, otherRtType); + } +#endif // FEATURE_TYPEEQUIVALENCE + public override Type BaseType => GetBaseType(); private RuntimeType GetBaseType() diff --git a/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 1325aa8..56694c6 100644 --- a/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -615,6 +615,11 @@ namespace System { throw new PlatformNotSupportedException(); } + +#if FEATURE_TYPEEQUIVALENCE + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern bool IsEquivalentTo(RuntimeType rtType1, RuntimeType rtType2); +#endif // FEATURE_TYPEEQUIVALENCE } // This type is used to remove the expense of having a managed reference object that is dynamically diff --git a/src/vm/ecalllist.h b/src/vm/ecalllist.h index 35fbae2..0298939 100644 --- a/src/vm/ecalllist.h +++ b/src/vm/ecalllist.h @@ -268,6 +268,7 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("Allocate", RuntimeTypeHandle::Allocate) //for A.CI FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal) + FCFuncElement("IsEquivalentTo", RuntimeTypeHandle::IsEquivalentTo) FCFuncEnd() FCFuncStart(gMetaDataImport) diff --git a/src/vm/runtimehandles.cpp b/src/vm/runtimehandles.cpp index dda5d1d..c8782db 100644 --- a/src/vm/runtimehandles.cpp +++ b/src/vm/runtimehandles.cpp @@ -250,6 +250,25 @@ FCIMPL1_V(EnregisteredTypeHandle, RuntimeTypeHandle::GetValueInternal, FCALLRunt } FCIMPLEND +FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::IsEquivalentTo, ReflectClassBaseObject *rtType1UNSAFE, ReflectClassBaseObject *rtType2UNSAFE) +{ + FCALL_CONTRACT; + + REFLECTCLASSBASEREF rtType1 = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(rtType1UNSAFE); + REFLECTCLASSBASEREF rtType2 = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(rtType2UNSAFE); + + BOOL areEquivalent = FALSE; + HELPER_METHOD_FRAME_BEGIN_RET_2(rtType1, rtType2); + + if (rtType1 != NULL && rtType2 != NULL) + areEquivalent = rtType1->GetType().IsEquivalentTo(rtType2->GetType()); + + HELPER_METHOD_FRAME_END(); + + FC_RETURN_BOOL(areEquivalent); +} +FCIMPLEND + // TypeEqualsHelper and TypeNotEqualsHelper are almost identical. // Unfortunately we cannot combime them because they need to hardcode the caller's name NOINLINE static BOOL TypeEqualSlow(OBJECTREF refL, OBJECTREF refR, LPVOID __me) diff --git a/src/vm/runtimehandles.h b/src/vm/runtimehandles.h index 6edf61e..66a27f4 100644 --- a/src/vm/runtimehandles.h +++ b/src/vm/runtimehandles.h @@ -152,6 +152,8 @@ public: static FCDECL1_V(ReflectClassBaseObject*, GetTypeFromHandle, FCALLRuntimeTypeHandle th); static FCDECL1_V(EnregisteredTypeHandle, GetValueInternal, FCALLRuntimeTypeHandle RTH); + static FCDECL2(FC_BOOL_RET, IsEquivalentTo, ReflectClassBaseObject *rtType1UNSAFE, ReflectClassBaseObject *rtType2UNSAFE); + static FCDECL2(FC_BOOL_RET, TypeEQ, Object* left, Object* right); static FCDECL2(FC_BOOL_RET, TypeNEQ, Object* left, Object* right); diff --git a/tests/src/baseservices/typeequivalence/simple/Simple.cs b/tests/src/baseservices/typeequivalence/simple/Simple.cs index 0b6a54d..56c3ecd 100644 --- a/tests/src/baseservices/typeequivalence/simple/Simple.cs +++ b/tests/src/baseservices/typeequivalence/simple/Simple.cs @@ -38,6 +38,34 @@ public class Simple } } + private static void ValidateTypeInstanceEquality() + { + Console.WriteLine($"{nameof(ValidateTypeInstanceEquality)}"); + var inAsm = EmptyType.Create(); + var otherAsm = EmptyType2.Create(); + + Type inAsmInterfaceType = inAsm.GetType().GetInterface(nameof(IEmptyType)); + Type otherAsmInterfaceType = otherAsm.GetType().GetInterface(nameof(IEmptyType)); + + // Sanity checks + Assert.IsTrue(inAsmInterfaceType == inAsmInterfaceType); + Assert.IsTrue(inAsmInterfaceType.IsEquivalentTo(inAsmInterfaceType)); + Assert.IsFalse(inAsmInterfaceType.IsEquivalentTo(inAsm.GetType())); + Assert.IsTrue(otherAsmInterfaceType == otherAsmInterfaceType); + Assert.IsTrue(otherAsmInterfaceType.IsEquivalentTo(otherAsmInterfaceType)); + Assert.IsFalse(otherAsmInterfaceType.IsEquivalentTo(otherAsm.GetType())); + + // The intrinsic equality operations should fail + Assert.IsFalse(inAsmInterfaceType == otherAsmInterfaceType); + Assert.IsFalse(inAsmInterfaceType.Equals(otherAsmInterfaceType)); + Assert.IsFalse(otherAsmInterfaceType == inAsmInterfaceType); + Assert.IsFalse(otherAsmInterfaceType.Equals(inAsmInterfaceType)); + + // Determination of equal types requires API call + Assert.IsTrue(inAsmInterfaceType.IsEquivalentTo(otherAsmInterfaceType)); + Assert.IsTrue(otherAsmInterfaceType.IsEquivalentTo(inAsmInterfaceType)); + } + private class MethodTestDerived : MethodTestBase { private readonly int scaleValue; @@ -127,6 +155,7 @@ public class Simple try { InterfaceTypesFromDifferentAssembliesAreEquivalent(); + ValidateTypeInstanceEquality(); InterfaceTypesMethodOperations(); CallSparseInterface(); } @@ -138,4 +167,4 @@ public class Simple return 100; } -} \ No newline at end of file +} -- 2.7.4