95fdb59ea6
Former-commit-id: b39a328747c2f3414dc52e009fb6f0aa80ca2492
680 lines
29 KiB
C#
680 lines
29 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Xml;
|
|
|
|
using Mono.Cecil;
|
|
using Mono.Collections.Generic;
|
|
using Mono.Documentation.Util;
|
|
|
|
namespace Mono.Documentation.Updater
|
|
{
|
|
public static class DocUtils
|
|
{
|
|
public static bool DoesNotHaveApiStyle (this XmlElement element, ApiStyle style)
|
|
{
|
|
string styleString = style.ToString ().ToLowerInvariant ();
|
|
string apistylevalue = element.GetAttribute ("apistyle");
|
|
return apistylevalue != styleString || string.IsNullOrWhiteSpace (apistylevalue);
|
|
}
|
|
public static bool HasApiStyle (this XmlElement element, ApiStyle style)
|
|
{
|
|
string styleString = style.ToString ().ToLowerInvariant ();
|
|
return element.GetAttribute ("apistyle") == styleString;
|
|
}
|
|
public static bool HasApiStyle (this XmlNode node, ApiStyle style)
|
|
{
|
|
var attribute = node.Attributes["apistyle"];
|
|
return attribute != null && attribute.Value == style.ToString ().ToLowerInvariant ();
|
|
}
|
|
public static void AddApiStyle (this XmlElement element, ApiStyle style)
|
|
{
|
|
string styleString = style.ToString ().ToLowerInvariant ();
|
|
var existingValue = element.GetAttribute ("apistyle");
|
|
if (string.IsNullOrWhiteSpace (existingValue) || existingValue != styleString)
|
|
{
|
|
element.SetAttribute ("apistyle", styleString);
|
|
}
|
|
|
|
// Propagate the API style up to the membernode if necessary
|
|
if (element.LocalName == "AssemblyInfo" && element.ParentNode != null && element.ParentNode.LocalName == "Member")
|
|
{
|
|
var member = element.ParentNode;
|
|
var unifiedAssemblyNode = member.SelectSingleNode ("AssemblyInfo[@apistyle='unified']");
|
|
var classicAssemblyNode = member.SelectSingleNode ("AssemblyInfo[not(@apistyle) or @apistyle='classic']");
|
|
|
|
var parentAttribute = element.ParentNode.Attributes["apistyle"];
|
|
Action removeStyle = () => element.ParentNode.Attributes.Remove (parentAttribute);
|
|
Action propagateStyle = () =>
|
|
{
|
|
if (parentAttribute == null)
|
|
{
|
|
// if it doesn't have the attribute, then add it
|
|
parentAttribute = element.OwnerDocument.CreateAttribute ("apistyle");
|
|
parentAttribute.Value = styleString;
|
|
element.ParentNode.Attributes.Append (parentAttribute);
|
|
}
|
|
};
|
|
|
|
if ((style == ApiStyle.Classic && unifiedAssemblyNode != null) || (style == ApiStyle.Unified && classicAssemblyNode != null))
|
|
removeStyle ();
|
|
else
|
|
propagateStyle ();
|
|
}
|
|
}
|
|
public static string GetFormattedTypeName(string name)
|
|
{
|
|
int index = name.IndexOf("`", StringComparison.Ordinal);
|
|
if (index >= 0)
|
|
return name.Substring(0, index);
|
|
|
|
return name;
|
|
}
|
|
public static void AddApiStyle (this XmlNode node, ApiStyle style)
|
|
{
|
|
string styleString = style.ToString ().ToLowerInvariant ();
|
|
var existingAttribute = node.Attributes["apistyle"];
|
|
if (existingAttribute == null)
|
|
{
|
|
existingAttribute = node.OwnerDocument.CreateAttribute ("apistyle");
|
|
node.Attributes.Append (existingAttribute);
|
|
}
|
|
existingAttribute.Value = styleString;
|
|
}
|
|
public static void RemoveApiStyle (this XmlElement element, ApiStyle style)
|
|
{
|
|
string styleString = style.ToString ().ToLowerInvariant ();
|
|
string existingValue = element.GetAttribute ("apistyle");
|
|
if (string.IsNullOrWhiteSpace (existingValue) || existingValue == styleString)
|
|
{
|
|
element.RemoveAttribute ("apistyle");
|
|
}
|
|
}
|
|
public static void RemoveApiStyle (this XmlNode node, ApiStyle style)
|
|
{
|
|
var styleAttribute = node.Attributes["apistyle"];
|
|
if (styleAttribute != null && styleAttribute.Value == style.ToString ().ToLowerInvariant ())
|
|
{
|
|
node.Attributes.Remove (styleAttribute);
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<T> SafeCast<T> (this System.Collections.IEnumerable list)
|
|
{
|
|
if (list == null) yield break;
|
|
|
|
foreach (object item in list)
|
|
{
|
|
if (item is T castedItem)
|
|
{
|
|
yield return castedItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool IsExplicitlyImplemented (MethodDefinition method)
|
|
{
|
|
return method != null && method.IsPrivate && method.IsFinal && method.IsVirtual;
|
|
}
|
|
|
|
public static string GetTypeDotMember (string name)
|
|
{
|
|
int startType, startMethod;
|
|
startType = startMethod = -1;
|
|
for (int i = 0; i < name.Length; ++i)
|
|
{
|
|
if (name[i] == '.')
|
|
{
|
|
startType = startMethod;
|
|
startMethod = i;
|
|
}
|
|
}
|
|
return name.Substring (startType + 1);
|
|
}
|
|
|
|
public static string GetMember(string name)
|
|
{
|
|
int i = name.LastIndexOf('.');
|
|
var memberName = i == -1 ? name : name.Substring(i + 1);
|
|
|
|
return memberName;
|
|
}
|
|
|
|
public static string GetMemberForProperty(string name)
|
|
{
|
|
int i = name.LastIndexOf('.');
|
|
var memberName = i == -1 ? name : name.Substring(i + 1);
|
|
|
|
if (memberName.StartsWith("get_") || memberName.StartsWith("set_") || memberName.StartsWith("put_"))
|
|
{
|
|
var index = memberName.IndexOf("_", StringComparison.InvariantCulture);
|
|
if (index > 0)
|
|
//remove get/set prefix from method name
|
|
memberName = memberName.Substring(index + 1);
|
|
}
|
|
|
|
return memberName;
|
|
}
|
|
|
|
public static void GetInfoForExplicitlyImplementedMethod (
|
|
MethodDefinition method, out TypeReference iface, out MethodReference ifaceMethod)
|
|
{
|
|
iface = null;
|
|
ifaceMethod = null;
|
|
if (method.Overrides.Count != 1)
|
|
throw new InvalidOperationException ("Could not determine interface type for explicitly-implemented interface member " + method.Name);
|
|
iface = method.Overrides[0].DeclaringType;
|
|
ifaceMethod = method.Overrides[0];
|
|
}
|
|
|
|
public static bool IsPublic (TypeDefinition type)
|
|
{
|
|
TypeDefinition decl = type;
|
|
while (decl != null)
|
|
{
|
|
if (!(decl.IsPublic || decl.IsNestedPublic ||
|
|
decl.IsNestedFamily || decl.IsNestedFamily || decl.IsNestedFamilyOrAssembly))
|
|
{
|
|
return false;
|
|
}
|
|
decl = (TypeDefinition)decl.DeclaringType;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static string GetPropertyName (PropertyDefinition pi, string delimeter = ".")
|
|
{
|
|
// Issue: (g)mcs-generated assemblies that explicitly implement
|
|
// properties don't specify the full namespace, just the
|
|
// TypeName.Property; .NET uses Full.Namespace.TypeName.Property.
|
|
MethodDefinition method = pi.GetMethod ?? pi.SetMethod;
|
|
bool isExplicitlyImplemented = IsExplicitlyImplemented(method);
|
|
if (!isExplicitlyImplemented)
|
|
return pi.Name;
|
|
|
|
// Need to determine appropriate namespace for this member.
|
|
GetInfoForExplicitlyImplementedMethod (method, out var iface, out var ifaceMethod);
|
|
var stringifyIface = DocTypeFullMemberFormatter.Default.GetName(iface).Replace(".", delimeter);
|
|
return string.Join (delimeter, new string[]{
|
|
stringifyIface,
|
|
GetMemberForProperty (ifaceMethod.Name)});
|
|
}
|
|
|
|
public static string GetNamespace (TypeReference type, string delimeter = null)
|
|
{
|
|
if (type == null)
|
|
return string.Empty;
|
|
|
|
if (type.GetElementType ().IsNested)
|
|
type = type.GetElementType ();
|
|
while (type != null && type.IsNested && !type.IsGenericParameter)
|
|
type = type.DeclaringType;
|
|
if (type == null)
|
|
return string.Empty;
|
|
|
|
string typeNS = type.Namespace;
|
|
|
|
if (!string.IsNullOrEmpty(delimeter))
|
|
{
|
|
typeNS = typeNS.Replace(".", delimeter);
|
|
}
|
|
|
|
// first, make sure this isn't a type reference to another assembly/module
|
|
|
|
bool isInAssembly = MDocUpdater.IsInAssemblies (type.Module.Name);
|
|
if (isInAssembly && !typeNS.StartsWith ("System") && MDocUpdater.HasDroppedNamespace (type))
|
|
{
|
|
typeNS = string.Format ("{0}{1}{2}", MDocUpdater.droppedNamespace, delimeter ?? ".", typeNS);
|
|
}
|
|
return typeNS;
|
|
}
|
|
|
|
public static string PathCombine (string dir, string path)
|
|
{
|
|
if (dir == null)
|
|
dir = "";
|
|
if (path == null)
|
|
path = "";
|
|
return Path.Combine (dir, path);
|
|
}
|
|
|
|
public static bool IsExtensionMethod (MethodDefinition method)
|
|
{
|
|
return
|
|
method.CustomAttributes
|
|
.Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute")
|
|
&& method.DeclaringType.CustomAttributes
|
|
.Any (m => m.AttributeType.FullName == "System.Runtime.CompilerServices.ExtensionAttribute");
|
|
}
|
|
|
|
public static bool IsDelegate (TypeDefinition type)
|
|
{
|
|
TypeReference baseRef = type.BaseType;
|
|
if (baseRef == null)
|
|
return false;
|
|
return !type.IsAbstract && baseRef.FullName == "System.Delegate" || // FIXME
|
|
baseRef.FullName == "System.MulticastDelegate";
|
|
}
|
|
|
|
public static bool NeedsOverwrite(XmlElement element)
|
|
{
|
|
return element != null &&
|
|
!(element.HasAttribute("overwrite") &&
|
|
element.Attributes["overwrite"].Value.Equals("false", StringComparison.InvariantCultureIgnoreCase));
|
|
}
|
|
|
|
public static List<TypeReference> GetDeclaringTypes (TypeReference type)
|
|
{
|
|
List<TypeReference> decls = new List<TypeReference> ();
|
|
decls.Add (type);
|
|
while (type.DeclaringType != null)
|
|
{
|
|
decls.Add (type.DeclaringType);
|
|
type = type.DeclaringType;
|
|
}
|
|
decls.Reverse ();
|
|
return decls;
|
|
}
|
|
|
|
public static int GetGenericArgumentCount (TypeReference type)
|
|
{
|
|
GenericInstanceType inst = type as GenericInstanceType;
|
|
return inst != null
|
|
? inst.GenericArguments.Count
|
|
: type.GenericParameters.Count;
|
|
}
|
|
|
|
class TypeEquality : IEqualityComparer<TypeReference>
|
|
{
|
|
bool IEqualityComparer<TypeReference>.Equals (TypeReference x, TypeReference y)
|
|
{
|
|
if (x is null && y is null) return true;
|
|
if (x is null || y is null) return false;
|
|
return x.FullName == y.FullName;
|
|
}
|
|
|
|
int IEqualityComparer<TypeReference>.GetHashCode (TypeReference obj)
|
|
{
|
|
return obj.GetHashCode ();
|
|
}
|
|
}
|
|
static TypeEquality typeEqualityComparer = new TypeEquality ();
|
|
|
|
public static IEnumerable<TypeReference> GetAllPublicInterfaces (TypeDefinition type)
|
|
{
|
|
return GetAllInterfacesFromType (type)
|
|
.Where (i => IsPublic (i.Resolve ()))
|
|
.Distinct (typeEqualityComparer);
|
|
}
|
|
|
|
private static IEnumerable<TypeReference> GetAllInterfacesFromType(TypeDefinition type)
|
|
{
|
|
if (type is null)
|
|
yield break;
|
|
|
|
foreach(var i in type.Interfaces)
|
|
{
|
|
yield return i.InterfaceType;
|
|
foreach(var ii in GetAllInterfacesFromType(i.InterfaceType.Resolve()))
|
|
{
|
|
yield return ii;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<TypeReference> GetUserImplementedInterfaces (TypeDefinition type)
|
|
{
|
|
HashSet<string> inheritedInterfaces = GetInheritedInterfaces (type);
|
|
List<TypeReference> userInterfaces = new List<TypeReference> ();
|
|
foreach (var ii in type.Interfaces)
|
|
{
|
|
var iface = ii.InterfaceType;
|
|
TypeReference lookup = iface.Resolve () ?? iface;
|
|
if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
|
|
userInterfaces.Add (iface);
|
|
}
|
|
return userInterfaces.Where (i => IsPublic (i.Resolve ()));
|
|
}
|
|
|
|
private static string GetQualifiedTypeName (TypeReference type)
|
|
{
|
|
return "[" + type.Scope.Name + "]" + type.FullName;
|
|
}
|
|
|
|
private static HashSet<string> GetInheritedInterfaces (TypeDefinition type)
|
|
{
|
|
HashSet<string> inheritedInterfaces = new HashSet<string> ();
|
|
Action<TypeDefinition> a = null;
|
|
a = t =>
|
|
{
|
|
if (t == null) return;
|
|
foreach (var r in t.Interfaces)
|
|
{
|
|
inheritedInterfaces.Add (GetQualifiedTypeName (r.InterfaceType));
|
|
a (r.InterfaceType.Resolve ());
|
|
}
|
|
};
|
|
TypeReference baseRef = type.BaseType;
|
|
while (baseRef != null)
|
|
{
|
|
TypeDefinition baseDef = baseRef.Resolve ();
|
|
if (baseDef != null)
|
|
{
|
|
a (baseDef);
|
|
baseRef = baseDef.BaseType;
|
|
}
|
|
else
|
|
baseRef = null;
|
|
}
|
|
foreach (var r in type.Interfaces)
|
|
a (r.InterfaceType.Resolve ());
|
|
return inheritedInterfaces;
|
|
}
|
|
|
|
public static void AppendFieldValue(StringBuilder buf, FieldDefinition field)
|
|
{
|
|
// enums have a value__ field, which we ignore
|
|
if (((TypeDefinition)field.DeclaringType).IsEnum ||
|
|
field.DeclaringType.IsGenericType())
|
|
return;
|
|
if (field.HasConstant && field.IsLiteral)
|
|
{
|
|
object val = null;
|
|
try
|
|
{
|
|
val = field.Constant;
|
|
}
|
|
catch
|
|
{
|
|
return;
|
|
}
|
|
if (val == null)
|
|
buf.Append(" = ").Append("null");
|
|
else if (val is Enum)
|
|
buf.Append(" = ").Append(val.ToString());
|
|
else if (val is IFormattable)
|
|
{
|
|
string value = ((IFormattable)val).ToString(null, CultureInfo.InvariantCulture);
|
|
if (val is string)
|
|
value = "\"" + value + "\"";
|
|
buf.Append(" = ").Append(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// XPath is invalid if it containt '-symbol inside '...'.
|
|
/// So, put string which contains '-symbol inside "...", and vice versa
|
|
/// </summary>
|
|
public static string GetStringForXPath(string input)
|
|
{
|
|
if (!input.Contains("'"))
|
|
return $"\'{input}\'";
|
|
if (!input.Contains("\""))
|
|
return $"\"{input}\"";
|
|
return input;
|
|
}
|
|
|
|
/// <summary>
|
|
/// No documentation for property/event accessors.
|
|
/// </summary>
|
|
public static bool IsIgnored(MemberReference mi)
|
|
{
|
|
if (IsCompilerGenerated(mi))
|
|
{
|
|
if (mi.Name.StartsWith("get_", StringComparison.Ordinal)) return true;
|
|
if (mi.Name.StartsWith("set_", StringComparison.Ordinal)) return true;
|
|
if (mi.Name.StartsWith("put_", StringComparison.Ordinal)) return true;
|
|
if (mi.Name.StartsWith("add_", StringComparison.Ordinal)) return true;
|
|
if (mi.Name.StartsWith("remove_", StringComparison.Ordinal)) return true;
|
|
if (mi.Name.StartsWith("raise_", StringComparison.Ordinal)) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsCompilerGenerated(MemberReference mi)
|
|
{
|
|
IMemberDefinition memberDefinition = mi.Resolve();
|
|
return memberDefinition.IsSpecialName
|
|
|| memberDefinition.CustomAttributes.Any(i =>
|
|
i.AttributeType.FullName == Consts.CompilerGeneratedAttribute
|
|
|| i.AttributeType.FullName == Consts.CompilationMappingAttribute
|
|
);
|
|
}
|
|
|
|
public static bool IsAvailablePropertyMethod(MethodDefinition method)
|
|
{
|
|
return method != null
|
|
&& (IsExplicitlyImplemented(method)
|
|
|| (!method.IsPrivate && !method.IsAssembly && !method.IsFamilyAndAssembly));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all members of implemented interfaces as Dictionary [fingerprint string] -> MemberReference
|
|
/// </summary>
|
|
public static Dictionary<string, List<MemberReference>> GetImplementedMembersFingerprintLookup(TypeDefinition type)
|
|
{
|
|
var lookup = new Dictionary<string, List<MemberReference>>();
|
|
List<TypeDefinition> previousInterfaces = new List<TypeDefinition>();
|
|
foreach (var implementedInterface in type.Interfaces)
|
|
{
|
|
var interfaceType = implementedInterface.InterfaceType.Resolve();
|
|
if (interfaceType == null)
|
|
continue;
|
|
|
|
//Don't add duplicates of members which appear because of inheritance of interfaces
|
|
bool addDuplicates = !previousInterfaces.Any(
|
|
i => i.Interfaces.Any(
|
|
j => j.InterfaceType.FullName == interfaceType.FullName));
|
|
var genericInstanceType = implementedInterface.InterfaceType as GenericInstanceType;
|
|
foreach (var memberReference in interfaceType.GetMembers())
|
|
{
|
|
// pass genericInstanceType to resolve generic types if they are explicitly specified in the interface implementation code
|
|
var fingerprint = GetFingerprint(memberReference, genericInstanceType);
|
|
if (!lookup.ContainsKey(fingerprint))
|
|
lookup[fingerprint] = new List<MemberReference>();
|
|
|
|
// if it's going to be the first element with this fingerprint, add it anyway.
|
|
// otherwise, check addDuplicates flag
|
|
if (!lookup[fingerprint].Any() || addDuplicates)
|
|
lookup[fingerprint].Add(memberReference);
|
|
}
|
|
previousInterfaces.Add(interfaceType);
|
|
}
|
|
return lookup;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get fingerprint of MemberReference. If fingerprints are equal, members can be implementations of each other
|
|
/// </summary>
|
|
/// <param name="memberReference">Type member which fingerprint is returned</param>
|
|
/// <param name="genericInterface">GenericInstanceType instance to resolve generic types if they are explicitly specified in the interface implementation code</param>
|
|
/// <remarks>Any existing MemberFormatter can't be used for generation of fingerprint because none of them generate equal signatures
|
|
/// for an interface member and its implementation in the following cases:
|
|
/// 1. Explicitly implemented members
|
|
/// 2. Different names of generic arguments in interface and in implementing type
|
|
/// 3. Implementation of interface with generic type parameters
|
|
/// The last point is especially interesting because it makes GetFingerprint method context-sensitive:
|
|
/// it returns signatures of interface members depending on generic parameters of the implementing type (GenericInstanceType parameter).</remarks>
|
|
public static string GetFingerprint(MemberReference memberReference, GenericInstanceType genericInterface = null)
|
|
{
|
|
// An interface contains only the signatures of methods, properties, events or indexers.
|
|
|
|
StringBuilder buf = new StringBuilder();
|
|
|
|
var unifiedTypeNames = new Dictionary<string, string>();
|
|
FillUnifiedMemberTypeNames(unifiedTypeNames, memberReference as IGenericParameterProvider);// Fill the member generic parameters unified names as M0, M1....
|
|
FillUnifiedTypeNames(unifiedTypeNames, memberReference.DeclaringType, genericInterface);// Fill the type generic parameters unified names as T0, T1....
|
|
|
|
switch (memberReference)
|
|
{
|
|
case IMethodSignature methodSignature:
|
|
buf.Append(GetUnifiedTypeName(methodSignature.ReturnType, unifiedTypeNames)).Append(" ");
|
|
buf.Append(SimplifyName(memberReference.Name)).Append(" ");
|
|
AppendParameters(buf, methodSignature.Parameters, unifiedTypeNames);
|
|
break;
|
|
case PropertyDefinition propertyReference:
|
|
buf.Append(GetUnifiedTypeName(propertyReference.PropertyType, unifiedTypeNames)).Append(" ");
|
|
if (propertyReference.GetMethod != null)
|
|
buf.Append("get").Append(" ");
|
|
if (propertyReference.SetMethod != null)
|
|
buf.Append("set").Append(" ");
|
|
buf.Append(SimplifyName(memberReference.Name)).Append(" ");
|
|
AppendParameters(buf, propertyReference.Parameters, unifiedTypeNames);
|
|
break;
|
|
case EventDefinition eventReference:
|
|
buf.Append(GetUnifiedTypeName(eventReference.EventType, unifiedTypeNames)).Append(" ");
|
|
buf.Append(SimplifyName(memberReference.Name)).Append(" ");
|
|
break;
|
|
}
|
|
|
|
var memberUnifiedTypeNames = new Dictionary<string, string>();
|
|
FillUnifiedMemberTypeNames(memberUnifiedTypeNames, memberReference as IGenericParameterProvider);
|
|
if (memberUnifiedTypeNames.Any())// Add generic arguments to the fingerprint. SomeMethod<T>() != SomeMethod()
|
|
{
|
|
buf.Append(" ").Append(string.Join(" ", memberUnifiedTypeNames.Values));
|
|
}
|
|
return buf.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// If it's the name of explicitly implemented member return only short name
|
|
/// </summary>
|
|
private static string SimplifyName(string memberName)
|
|
{
|
|
int nameBorder = memberName.LastIndexOf(".", StringComparison.InvariantCulture);
|
|
if (nameBorder > 0)
|
|
{
|
|
return memberName.Substring(nameBorder + 1, memberName.Length - nameBorder - 1);
|
|
}
|
|
return memberName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resolve generic type names of the type type based on interface implementation details
|
|
/// </summary>
|
|
private static void FillUnifiedTypeNames(Dictionary<string, string> unifiedTypeNames,
|
|
IGenericParameterProvider type,
|
|
GenericInstanceType genericInterface)
|
|
{
|
|
if (type == null)
|
|
return;
|
|
|
|
int i = 0;
|
|
foreach (var genericParameter in type.GenericParameters)
|
|
{
|
|
if (unifiedTypeNames.ContainsKey(genericParameter.Name))
|
|
continue;
|
|
|
|
// if generic type can be resolved with interface implementation parameters
|
|
if (genericInterface != null
|
|
&& i < genericInterface.GenericArguments.Count)
|
|
{
|
|
unifiedTypeNames[genericParameter.Name] = genericInterface.GenericArguments[i].FullName;
|
|
}
|
|
else
|
|
{
|
|
unifiedTypeNames[genericParameter.Name] = genericParameter.Name;
|
|
}
|
|
++i;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resolve generic type names of the member based on the order of generic arguments
|
|
/// </summary>
|
|
private static void FillUnifiedMemberTypeNames(Dictionary<string, string> unifiedTypeNames,
|
|
IGenericParameterProvider member)
|
|
{
|
|
if (member == null)
|
|
return;
|
|
|
|
int i = 0;
|
|
foreach (var genericParameter in member.GenericParameters)
|
|
{
|
|
if (unifiedTypeNames.ContainsKey(genericParameter.Name))
|
|
continue;
|
|
unifiedTypeNames[genericParameter.Name] = "MemberGenericType" + i;
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
private static void AppendParameters(StringBuilder buf, Collection<ParameterDefinition> parameters, Dictionary<string, string> genericTypes)
|
|
{
|
|
buf.Append("(");
|
|
foreach (var parameterDefinition in parameters)
|
|
{
|
|
buf.Append(GetUnifiedTypeName(parameterDefinition.ParameterType, genericTypes)).Append(", ");
|
|
}
|
|
buf.Append(")");
|
|
}
|
|
|
|
private static string GetUnifiedTypeName(TypeReference type, Dictionary<string, string> genericTypes)
|
|
{
|
|
var genericInstance = type as IGenericInstance;
|
|
if (genericInstance != null && genericInstance.HasGenericArguments)
|
|
{
|
|
return
|
|
$"{type.Namespace}.{type.Name}<{string.Join(",", genericInstance.GenericArguments.Select(i => GetUnifiedTypeName(i, genericTypes)))}>";
|
|
}
|
|
|
|
return genericTypes.ContainsKey(type.Name)
|
|
? genericTypes[type.Name]
|
|
: type.FullName;
|
|
}
|
|
|
|
public static string GetExplicitTypeName(MemberReference memberReference)
|
|
{
|
|
var overrides = GetOverrides(memberReference);
|
|
if (overrides == null)
|
|
{
|
|
throw new ArgumentException("Unsupported explicitly implemented member");
|
|
}
|
|
|
|
var declaringType = overrides.First().DeclaringType;
|
|
return declaringType.GetElementType().FullName;
|
|
}
|
|
|
|
public static bool IsExplicitlyImplemented(MemberReference memberReference)
|
|
{
|
|
var overrides = GetOverrides(memberReference);
|
|
return overrides?.Count > 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get which members are overriden by memberReference
|
|
/// </summary>
|
|
private static Collection<MethodReference> GetOverrides(MemberReference memberReference)
|
|
{
|
|
switch (memberReference)
|
|
{
|
|
case MethodDefinition methodDefinition:
|
|
return methodDefinition.Overrides;
|
|
case PropertyDefinition propertyDefinition:
|
|
return (propertyDefinition.GetMethod ?? propertyDefinition.SetMethod)?.Overrides;
|
|
case EventDefinition evendDefinition:
|
|
return evendDefinition.AddMethod.Overrides;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static bool IsDestructor(MethodDefinition method)
|
|
{
|
|
return method.IsFamily
|
|
&& method.Name == "Finalize"
|
|
&& method.Overrides.Count == 1
|
|
&& method.Overrides[0].DeclaringType.FullName == "System.Object";
|
|
}
|
|
|
|
public static bool IsOperator(MethodReference method)
|
|
{
|
|
return method.Name.StartsWith("op_", StringComparison.Ordinal);
|
|
}
|
|
}
|
|
} |