2021-09-20 18:20:01 +02:00

222 lines
6.2 KiB
C#

// dnlib: See LICENSE.txt for more info
// See coreclr/src/vm/siginfo.cpp
using System;
using System.Diagnostics;
namespace dnlib.DotNet {
/// <summary>
/// <c>System.Runtime.InteropServices.TypeIdentifierAttribute</c> helper code used by <see cref="SigComparer"/>
/// </summary>
static class TIAHelper {
readonly struct Info : IEquatable<Info> {
public readonly UTF8String? Scope;
public readonly UTF8String? Identifier;
public Info(UTF8String? scope, UTF8String? identifier) {
Scope = scope;
Identifier = identifier;
}
public bool Equals(Info other) => stricmp(Scope, other.Scope) && UTF8String.Equals(Identifier, other.Identifier);
static bool stricmp(UTF8String? a, UTF8String? b) {
var da = a?.Data;
var db = b?.Data;
if (da == db)
return true;
if (da == null || db == null)
return false;
if (da.Length != db.Length)
return false;
for (int i = 0; i < da.Length; i++) {
byte ba = da[i], bb = db[i];
if ((byte)'A' <= ba && ba <= (byte)'Z')
ba = (byte)(ba - 'A' + 'a');
if ((byte)'A' <= bb && bb <= (byte)'Z')
bb = (byte)(bb - 'A' + 'a');
if (ba != bb)
return false;
}
return true;
}
}
static Info? GetInfo(TypeDef td) {
if (td == null)
return null;
if (td.IsWindowsRuntime)
return null;
UTF8String? scope = null, identifier = null;
var tia = td.CustomAttributes.Find("System.Runtime.InteropServices.TypeIdentifierAttribute");
if (tia != null) {
if (tia.ConstructorArguments.Count >= 2) {
if (tia.ConstructorArguments[0].Type.GetElementType() != ElementType.String)
return null;
if (tia.ConstructorArguments[1].Type.GetElementType() != ElementType.String)
return null;
scope = tia.ConstructorArguments[0].Value as UTF8String ?? tia.ConstructorArguments[0].Value as string;
identifier = tia.ConstructorArguments[1].Value as UTF8String ?? tia.ConstructorArguments[1].Value as string;
}
}
else {
var asm = td.Module?.Assembly;
if (asm == null)
return null;
bool isTypeLib = asm.CustomAttributes.IsDefined("System.Runtime.InteropServices.ImportedFromTypeLibAttribute") ||
asm.CustomAttributes.IsDefined("System.Runtime.InteropServices.PrimaryInteropAssemblyAttribute");
if (!isTypeLib)
return null;
}
if (UTF8String.IsNull(identifier)) {
CustomAttribute gca;
if (td.IsInterface && td.IsImport)
gca = td.CustomAttributes.Find("System.Runtime.InteropServices.GuidAttribute");
else {
var asm = td.Module?.Assembly;
if (asm == null)
return null;
gca = asm.CustomAttributes.Find("System.Runtime.InteropServices.GuidAttribute");
}
if (gca == null)
return null;
if (gca.ConstructorArguments.Count < 1)
return null;
if (gca.ConstructorArguments[0].Type.GetElementType() != ElementType.String)
return null;
scope = gca.ConstructorArguments[0].Value as UTF8String ?? gca.ConstructorArguments[0].Value as string;
var ns = td.Namespace;
var name = td.Name;
if (UTF8String.IsNullOrEmpty(ns))
identifier = name;
else if (UTF8String.IsNullOrEmpty(name))
identifier = new UTF8String(Concat(ns.Data, (byte)'.', Array.Empty<byte>()));
else
identifier = new UTF8String(Concat(ns.Data, (byte)'.', name.Data));
}
return new Info(scope, identifier);
}
static byte[] Concat(byte[] a, byte b, byte[] c) {
var data = new byte[a.Length + 1 + c.Length];
for (int i = 0; i < a.Length; i++)
data[i] = a[i];
data[a.Length] = b;
for (int i = 0, j = a.Length + 1; i < c.Length; i++, j++)
data[j] = c[i];
return data;
}
internal static bool IsTypeDefEquivalent(TypeDef td) => GetInfo(td) != null && CheckEquivalent(td);
static bool CheckEquivalent(TypeDef td) {
Debug2.Assert(td != null);
for (int i = 0; td != null && i < 1000; i++) {
if (i != 0) {
var info = GetInfo(td);
if (info == null)
return false;
}
bool f;
if (td.IsInterface)
f = td.IsImport || td.CustomAttributes.IsDefined("System.Runtime.InteropServices.ComEventInterfaceAttribute");
else
f = td.IsValueType || td.IsDelegate;
if (!f)
return false;
if (td.GenericParameters.Count > 0)
return false;
var declType = td.DeclaringType;
if (declType == null)
return td.IsPublic;
if (!td.IsNestedPublic)
return false;
td = declType;
}
return false;
}
public static bool Equivalent(TypeDef td1, TypeDef td2) {
var info1 = GetInfo(td1);
if (info1 == null)
return false;
var info2 = GetInfo(td2);
if (info2 == null)
return false;
if (!CheckEquivalent(td1) || !CheckEquivalent(td2))
return false;
if (!info1.Value.Equals(info2.Value))
return false;
// Caller has already compared names of the types and any declaring types
for (int i = 0; i < 1000; i++) {
if (td1.IsInterface) {
if (!td2.IsInterface)
return false;
}
else {
var bt1 = td1.BaseType;
var bt2 = td2.BaseType;
if (bt1 == null || bt2 == null)
return false;
if (td1.IsDelegate) {
if (!td2.IsDelegate)
return false;
if (!DelegateEquals(td1, td2))
return false;
}
else if (td1.IsValueType) {
if (td1.IsEnum != td2.IsEnum)
return false;
if (!td2.IsValueType)
return false;
if (!ValueTypeEquals(td1, td2, td1.IsEnum))
return false;
}
else
return false;
}
td1 = td1.DeclaringType;
td2 = td2.DeclaringType;
if (td1 == null && td2 == null)
break;
if (td1 == null || td2 == null)
return false;
}
return true;
}
static bool DelegateEquals(TypeDef td1, TypeDef td2) {
var invoke1 = td1.FindMethod(InvokeString);
var invoke2 = td2.FindMethod(InvokeString);
if (invoke1 == null || invoke2 == null)
return false;
//TODO: Compare method signatures. Prevent infinite recursion...
return true;
}
static readonly UTF8String InvokeString = new UTF8String("Invoke");
static bool ValueTypeEquals(TypeDef td1, TypeDef td2, bool isEnum) {
if (td1.Methods.Count != 0 || td2.Methods.Count != 0)
return false;
//TODO: Compare the fields. Prevent infinite recursion...
return true;
}
}
}