304 lines
9.2 KiB
C#
304 lines
9.2 KiB
C#
![]() |
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||
|
//
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||
|
//
|
||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||
|
// substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||
|
// DEALINGS IN THE SOFTWARE.
|
||
|
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Linq;
|
||
|
using System.Text;
|
||
|
using dnlib.DotNet;
|
||
|
|
||
|
namespace dnSpy.Contracts.Decompiler.XmlDoc {
|
||
|
/// <summary>
|
||
|
/// Provides XML documentation tags.
|
||
|
/// </summary>
|
||
|
public sealed class XmlDocKeyProvider {
|
||
|
/// <summary>
|
||
|
/// Gets an XML doc key
|
||
|
/// </summary>
|
||
|
/// <param name="member">Member</param>
|
||
|
/// <param name="b">String builder</param>
|
||
|
/// <returns></returns>
|
||
|
public static StringBuilder? GetKey(IMemberRef? member, StringBuilder b) {
|
||
|
if (member is null)
|
||
|
return null;
|
||
|
b.Clear();
|
||
|
if (member is ITypeDefOrRef) {
|
||
|
b.Append("T:");
|
||
|
AppendTypeName(b, ((ITypeDefOrRef)member).ToTypeSig());
|
||
|
}
|
||
|
else {
|
||
|
if (member.IsField)
|
||
|
b.Append("F:");
|
||
|
else if (member.IsPropertyDef)
|
||
|
b.Append("P:");
|
||
|
else if (member.IsEventDef)
|
||
|
b.Append("E:");
|
||
|
else if (member.IsMethod)
|
||
|
b.Append("M:");
|
||
|
AppendTypeName(b, member.DeclaringType.ToTypeSig());
|
||
|
b.Append('.');
|
||
|
b.Append(member.Name.Replace('.', '#'));
|
||
|
IList<Parameter>? parameters;
|
||
|
TypeSig? explicitReturnType = null;
|
||
|
if (member.IsPropertyDef) {
|
||
|
parameters = GetParameters((PropertyDef)member).ToList();
|
||
|
}
|
||
|
else if (member.IsMethod) {
|
||
|
var mr = (IMethod)member;
|
||
|
if (mr.NumberOfGenericParameters > 0) {
|
||
|
b.Append("``");
|
||
|
b.Append(mr.NumberOfGenericParameters);
|
||
|
}
|
||
|
parameters = mr.GetParameters();
|
||
|
if (mr.Name == "op_Implicit" || mr.Name == "op_Explicit") {
|
||
|
explicitReturnType = mr.MethodSig.GetRetType();
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
parameters = null;
|
||
|
}
|
||
|
if (parameters is not null && parameters.Any(a => a.IsNormalMethodParameter)) {
|
||
|
b.Append('(');
|
||
|
for (int i = 0; i < parameters.Count; i++) {
|
||
|
var param = parameters[i];
|
||
|
if (!param.IsNormalMethodParameter)
|
||
|
continue;
|
||
|
if (param.MethodSigIndex > 0)
|
||
|
b.Append(',');
|
||
|
AppendTypeName(b, param.Type);
|
||
|
}
|
||
|
b.Append(')');
|
||
|
}
|
||
|
if (explicitReturnType is not null) {
|
||
|
b.Append('~');
|
||
|
AppendTypeName(b, explicitReturnType);
|
||
|
}
|
||
|
}
|
||
|
return b;
|
||
|
}
|
||
|
|
||
|
static IEnumerable<Parameter> GetParameters(PropertyDef property) {
|
||
|
if (property is null)
|
||
|
yield break;
|
||
|
if (property.GetMethod is not null) {
|
||
|
foreach (var param in property.GetMethod.Parameters)
|
||
|
yield return param;
|
||
|
yield break;
|
||
|
}
|
||
|
if (property.SetMethod is not null) {
|
||
|
int last = property.SetMethod.Parameters.Count - 1;
|
||
|
foreach (var param in property.SetMethod.Parameters) {
|
||
|
if (param.Index != last)
|
||
|
yield return param;
|
||
|
}
|
||
|
yield break;
|
||
|
}
|
||
|
|
||
|
int i = 0;
|
||
|
foreach (var param in property.PropertySig.GetParams()) {
|
||
|
yield return new Parameter(i, i, param);
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void AppendTypeName(StringBuilder b, TypeSig type) {
|
||
|
type = type.RemovePinnedAndModifiers();
|
||
|
if (type is null)
|
||
|
return;
|
||
|
if (type is GenericInstSig giType) {
|
||
|
AppendTypeNameWithArguments(b, giType.GenericType is null ? null : giType.GenericType.TypeDefOrRef, giType.GenericArguments);
|
||
|
return;
|
||
|
}
|
||
|
if (type is ArraySigBase arrayType) {
|
||
|
AppendTypeName(b, arrayType.Next);
|
||
|
b.Append('[');
|
||
|
var lowerBounds = arrayType.GetLowerBounds();
|
||
|
var sizes = arrayType.GetSizes();
|
||
|
for (int i = 0; i < arrayType.Rank; i++) {
|
||
|
if (i > 0)
|
||
|
b.Append(',');
|
||
|
if (i < lowerBounds.Count && i < sizes.Count) {
|
||
|
b.Append(lowerBounds[i]);
|
||
|
b.Append(':');
|
||
|
b.Append(sizes[i] + lowerBounds[i] - 1);
|
||
|
}
|
||
|
}
|
||
|
b.Append(']');
|
||
|
return;
|
||
|
}
|
||
|
if (type is ByRefSig refType) {
|
||
|
AppendTypeName(b, refType.Next);
|
||
|
b.Append('@');
|
||
|
return;
|
||
|
}
|
||
|
if (type is PtrSig ptrType) {
|
||
|
AppendTypeName(b, ptrType.Next);
|
||
|
b.Append('*');
|
||
|
return;
|
||
|
}
|
||
|
if (type is GenericSig gp) {
|
||
|
b.Append('`');
|
||
|
if (gp.IsMethodVar) {
|
||
|
b.Append('`');
|
||
|
}
|
||
|
b.Append(gp.Number);
|
||
|
}
|
||
|
else {
|
||
|
var typeRef = type.ToTypeDefOrRef();
|
||
|
if (typeRef.DeclaringType is not null) {
|
||
|
AppendTypeName(b, typeRef.DeclaringType.ToTypeSig());
|
||
|
b.Append('.');
|
||
|
b.Append(typeRef.Name);
|
||
|
}
|
||
|
else {
|
||
|
FullNameFactory.FullNameSB(type, false, null, null, null, b);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int AppendTypeNameWithArguments(StringBuilder b, ITypeDefOrRef? type, IList<TypeSig> genericArguments) {
|
||
|
if (type is null)
|
||
|
return 0;
|
||
|
int outerTypeParameterCount = 0;
|
||
|
if (type.DeclaringType is not null) {
|
||
|
ITypeDefOrRef declType = type.DeclaringType;
|
||
|
outerTypeParameterCount = AppendTypeNameWithArguments(b, declType, genericArguments);
|
||
|
b.Append('.');
|
||
|
}
|
||
|
else {
|
||
|
int len = b.Length;
|
||
|
FullNameFactory.NamespaceSB(type, true, b);
|
||
|
if (len != b.Length)
|
||
|
b.Append('.');
|
||
|
}
|
||
|
b.Append(SplitTypeParameterCountFromReflectionName(type.Name, out int localTypeParameterCount));
|
||
|
|
||
|
if (localTypeParameterCount > 0) {
|
||
|
int totalTypeParameterCount = outerTypeParameterCount + localTypeParameterCount;
|
||
|
b.Append('{');
|
||
|
for (int i = outerTypeParameterCount; i < totalTypeParameterCount && i < genericArguments.Count; i++) {
|
||
|
if (i > outerTypeParameterCount)
|
||
|
b.Append(',');
|
||
|
AppendTypeName(b, genericArguments[i]);
|
||
|
}
|
||
|
b.Append('}');
|
||
|
}
|
||
|
return outerTypeParameterCount + localTypeParameterCount;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Removes the ` with type parameter count from the reflection name.
|
||
|
/// </summary>
|
||
|
/// <remarks>Do not use this method with the full name of inner classes.</remarks>
|
||
|
public static string SplitTypeParameterCountFromReflectionName(string reflectionName, out int typeParameterCount) {
|
||
|
int pos = reflectionName.LastIndexOf('`');
|
||
|
if (pos < 0) {
|
||
|
typeParameterCount = 0;
|
||
|
return reflectionName;
|
||
|
}
|
||
|
else {
|
||
|
string typeCount = reflectionName.Substring(pos + 1);
|
||
|
if (int.TryParse(typeCount, out typeParameterCount))
|
||
|
return reflectionName.Substring(0, pos);
|
||
|
else
|
||
|
return reflectionName;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Finds a member by key
|
||
|
/// </summary>
|
||
|
/// <param name="module">Module to search</param>
|
||
|
/// <param name="key">Key</param>
|
||
|
/// <returns></returns>
|
||
|
public static IMemberRef? FindMemberByKey(ModuleDef module, string key) {
|
||
|
if (module is null)
|
||
|
throw new ArgumentNullException(nameof(module));
|
||
|
if (key is null || key.Length < 2 || key[1] != ':')
|
||
|
return null;
|
||
|
switch (key[0]) {
|
||
|
case 'T':
|
||
|
return FindType(module, key.Substring(2));
|
||
|
case 'F':
|
||
|
return FindMember(module, key, type => type.Fields);
|
||
|
case 'P':
|
||
|
return FindMember(module, key, type => type.Properties);
|
||
|
case 'E':
|
||
|
return FindMember(module, key, type => type.Events);
|
||
|
case 'M':
|
||
|
return FindMember(module, key, type => type.Methods);
|
||
|
default:
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static IMemberRef? FindMember(ModuleDef module, string key, Func<TypeDef, IEnumerable<IMemberRef>> memberSelector) {
|
||
|
int parenPos = key.IndexOf('(');
|
||
|
int dotPos;
|
||
|
if (parenPos > 0) {
|
||
|
dotPos = key.LastIndexOf('.', parenPos - 1, parenPos);
|
||
|
}
|
||
|
else {
|
||
|
dotPos = key.LastIndexOf('.');
|
||
|
}
|
||
|
if (dotPos < 0)
|
||
|
return null;
|
||
|
TypeDef? type = FindType(module, key.Substring(2, dotPos - 2));
|
||
|
if (type is null)
|
||
|
return null;
|
||
|
string shortName;
|
||
|
if (parenPos > 0) {
|
||
|
shortName = key.Substring(dotPos + 1, parenPos - (dotPos + 1));
|
||
|
}
|
||
|
else {
|
||
|
shortName = key.Substring(dotPos + 1);
|
||
|
}
|
||
|
IMemberRef? shortNameMatch = null;
|
||
|
var sb = new StringBuilder();
|
||
|
foreach (IMemberRef member in memberSelector(type)) {
|
||
|
var memberKey = GetKey(member, sb);
|
||
|
if (memberKey?.CheckEquals(key) == true)
|
||
|
return member;
|
||
|
if (shortName == member.Name.Replace('.', '#'))
|
||
|
shortNameMatch = member;
|
||
|
}
|
||
|
// if there's no match by ID string (key), return the match by name.
|
||
|
return shortNameMatch;
|
||
|
}
|
||
|
|
||
|
static TypeDef? FindType(ModuleDef module, string name) {
|
||
|
int pos = name.LastIndexOf('.');
|
||
|
if (string.IsNullOrEmpty(name))
|
||
|
return null;
|
||
|
TypeDef? type = module.Find(name, true);
|
||
|
if (type is null && pos > 0) { // Original code only entered if ns.Length > 0
|
||
|
// try if this is a nested type
|
||
|
type = FindType(module, name.Substring(0, pos));
|
||
|
if (type is not null) {
|
||
|
foreach (var nt in type.NestedTypes) {
|
||
|
if (nt.Name == name)
|
||
|
return nt;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
return type;
|
||
|
}
|
||
|
}
|
||
|
}
|