From 511f9dcde9e278c6224ed4d61febb72b2d5cf2c4 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 27 Jan 2022 15:34:16 -0800 Subject: [PATCH 1/3] ILVerify recognizes type equivalence of two types if they have a System.Runtime.InteropServices.TypeIdentifierAttribute --- .../Common/TypeSystem/Common/TypeDesc.cs | 22 +++++++- .../tools/Common/TypeSystem/Ecma/EcmaType.cs | 54 +++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TypeDesc.cs b/src/coreclr/tools/Common/TypeSystem/Common/TypeDesc.cs index 51cf7291cdd748..1916e53f7dccf9 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TypeDesc.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TypeDesc.cs @@ -25,6 +25,10 @@ public override bool Equals(object o) { // Its only valid to compare two TypeDescs in the same context Debug.Assert(o is not TypeDesc || object.ReferenceEquals(((TypeDesc)o).Context, this.Context)); + if (o is TypeDesc) + { + return this.IsEquivalentTo((TypeDesc)o); + } return object.ReferenceEquals(this, o); } @@ -33,14 +37,18 @@ public override bool Equals(object o) { // Its only valid to compare two TypeDescs in the same context Debug.Assert(left is null || right is null || object.ReferenceEquals(left.Context, right.Context)); - return object.ReferenceEquals(left, right); + if (left is null) + return object.ReferenceEquals(left, right); + return left.IsEquivalentTo(right); } public static bool operator !=(TypeDesc left, TypeDesc right) { // Its only valid to compare two TypeDescs in the same context Debug.Assert(left is null || right is null || object.ReferenceEquals(left.Context, right.Context)); - return !object.ReferenceEquals(left, right); + if (left is null) + return !object.ReferenceEquals(left, right); + return !left.IsEquivalentTo(right); } #endif @@ -692,5 +700,15 @@ public bool IsIDynamicInterfaceCastable return (GetTypeFlags(TypeFlags.IsIDynamicInterfaceCastable | TypeFlags.IsIDynamicInterfaceCastableComputed) & TypeFlags.IsIDynamicInterfaceCastable) != 0; } } + + /// + /// Determines whether two types have the same identity and are eligible for type equivalence. + /// + public virtual bool IsEquivalentTo(TypeDesc typeDesc) + { + // Its only valid to compare two TypeDescs in the same context + Debug.Assert(typeDesc is null || object.ReferenceEquals(this.Context, typeDesc.Context)); + return object.ReferenceEquals(this, typeDesc); + } } } diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaType.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaType.cs index 073cb7a4fdfb2b..e453f1278d74f1 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaType.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaType.cs @@ -604,5 +604,59 @@ public override PInvokeStringFormat PInvokeStringFormat return (PInvokeStringFormat)(_typeDefinition.Attributes & TypeAttributes.StringFormatMask); } } + + public override bool IsEquivalentTo(TypeDesc typeDesc) + { + if (base.IsEquivalentTo(typeDesc)) + return true; + + if (typeDesc is EcmaType) + { + return IsPossiblyEquivalent(this, (EcmaType)typeDesc); + } + + return false; + + static bool IsPossiblyEquivalent(EcmaType type1, EcmaType type2) + { + if (!type1.HasCustomAttribute("System.Runtime.InteropServices", "TypeIdentifierAttribute")) + return false; + + if (!type2.HasCustomAttribute("System.Runtime.InteropServices", "TypeIdentifierAttribute")) + return false; + + var attrOpt1 = type1.GetDecodedCustomAttribute("System.Runtime.InteropServices", "TypeIdentifierAttribute"); + var attrOpt2 = type2.GetDecodedCustomAttribute("System.Runtime.InteropServices", "TypeIdentifierAttribute"); + Debug.Assert(attrOpt1.HasValue); + Debug.Assert(attrOpt2.HasValue); + var attr1 = attrOpt1.Value; + var attr2 = attrOpt2.Value; + + if (attr1.FixedArguments.Length != 2 || attr2.FixedArguments.Length != 2) + return false; + + var scope1 = attr1.FixedArguments[0].Value as string; + var identifier1 = attr1.FixedArguments[1].Value as string; + var scope2 = attr2.FixedArguments[0].Value as string; + var identifier2 = attr2.FixedArguments[1].Value as string; + + if (scope1 == null || identifier1 == null || scope2 == null || identifier2 == null) + return false; + + if (identifier1 != identifier2) + return false; + + if (!Guid.TryParse(scope1, out var scopeGuidValue1)) + return false; + + if (!Guid.TryParse(scope2, out var scopeGuidValue2)) + return false; + + if (scopeGuidValue1 != scopeGuidValue2) + return false; + + return true; + } + } } } From 6f2c03eab5cc7d315de6189a71c407ba8d85bbaf Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 27 Jan 2022 15:48:41 -0800 Subject: [PATCH 2/3] Minor cleanup --- src/coreclr/tools/Common/TypeSystem/Common/TypeDesc.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Common/TypeDesc.cs b/src/coreclr/tools/Common/TypeSystem/Common/TypeDesc.cs index 1916e53f7dccf9..480739bb516cf5 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/TypeDesc.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/TypeDesc.cs @@ -26,9 +26,7 @@ public override bool Equals(object o) // Its only valid to compare two TypeDescs in the same context Debug.Assert(o is not TypeDesc || object.ReferenceEquals(((TypeDesc)o).Context, this.Context)); if (o is TypeDesc) - { return this.IsEquivalentTo((TypeDesc)o); - } return object.ReferenceEquals(this, o); } From a30fcb06afb5e9a0e8e0fdd4f884c64b4020b4f6 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Thu, 27 Jan 2022 15:50:08 -0800 Subject: [PATCH 3/3] Minor cleanup --- src/coreclr/tools/Common/TypeSystem/Ecma/EcmaType.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaType.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaType.cs index e453f1278d74f1..9a34a70a6c2263 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaType.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaType.cs @@ -611,13 +611,11 @@ public override bool IsEquivalentTo(TypeDesc typeDesc) return true; if (typeDesc is EcmaType) - { - return IsPossiblyEquivalent(this, (EcmaType)typeDesc); - } + return CheckEquivalence(this, (EcmaType)typeDesc); return false; - static bool IsPossiblyEquivalent(EcmaType type1, EcmaType type2) + static bool CheckEquivalence(EcmaType type1, EcmaType type2) { if (!type1.HasCustomAttribute("System.Runtime.InteropServices", "TypeIdentifierAttribute")) return false;