|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Diagnostics;
using System.Text;
using Internal.Metadata.NativeFormat;
namespace Internal.StackTraceMetadata
{
internal class MethodNameFormatter
{
[Flags]
private enum Flags
{
None = 0,
NamespaceQualify = 1,
ReflectionFormat = 2,
}
/// <summary>
/// Metadata reader used for the purpose of method name formatting.
/// </summary>
private readonly MetadataReader _metadataReader;
/// <summary>
/// String builder used to construct formatted method name.
/// </summary>
private readonly StringBuilder _outputBuilder;
/// <summary>
/// Represents the instatiation type context.
/// </summary>
private readonly SigTypeContext _typeContext;
/// <summary>
/// Initialize the reader used for method name formatting.
/// </summary>
private MethodNameFormatter(MetadataReader metadataReader, SigTypeContext typeContext)
{
_metadataReader = metadataReader;
_outputBuilder = new StringBuilder();
_typeContext = typeContext;
}
public static string FormatReflectionNotationTypeName(MetadataReader metadataReader, Handle type)
{
MethodNameFormatter formatter = new MethodNameFormatter(metadataReader, default);
formatter.EmitTypeName(type, Flags.NamespaceQualify | Flags.ReflectionFormat);
return formatter._outputBuilder.ToString();
}
public static (string, string, string, string) FormatMethodName(MetadataReader metadataReader, Handle owningType, ConstantStringValueHandle name, MethodSignatureHandle signature, ConstantStringArrayHandle genericArguments)
{
MethodNameFormatter formatter = new MethodNameFormatter(metadataReader, SigTypeContext.FromMethod(metadataReader, owningType, genericArguments));
formatter.EmitTypeName(owningType, Flags.NamespaceQualify);
string owningTypeString = formatter._outputBuilder.ToString();
formatter._outputBuilder.Clear();
string methodName = metadataReader.GetString(name);
string genericArgs = null;
if (!genericArguments.IsNil)
{
var args = metadataReader.GetConstantStringArray(genericArguments);
bool first = true;
foreach (Handle handle in args.Value)
{
if (!first)
formatter._outputBuilder.Append(',');
else
first = false;
formatter.EmitString(handle.ToConstantStringValueHandle(metadataReader));
}
genericArgs = formatter._outputBuilder.ToString();
formatter._outputBuilder.Clear();
}
formatter.EmitTypeVector(metadataReader.GetMethodSignature(signature).Parameters);
string signatureString = formatter._outputBuilder.ToString();
return (owningTypeString, genericArgs, methodName, signatureString);
}
public static (string, string, string, string) FormatMethodName(MetadataReader metadataReader, TypeDefinitionHandle enclosingTypeHandle, MethodHandle methodHandle)
{
MethodNameFormatter formatter = new MethodNameFormatter(metadataReader, SigTypeContext.FromMethod(metadataReader, enclosingTypeHandle, methodHandle));
Method method = metadataReader.GetMethod(methodHandle);
formatter.EmitTypeName(enclosingTypeHandle, Flags.NamespaceQualify);
string owningTypeString = formatter._outputBuilder.ToString();
formatter._outputBuilder.Clear();
string methodName = metadataReader.GetString(method.Name);
string genericArgs = null;
if (method.GenericParameters.Count > 0)
{
bool first = true;
foreach (GenericParameterHandle handle in method.GenericParameters)
{
if (first)
first = false;
else
formatter._outputBuilder.Append(',');
formatter.EmitTypeName(handle, Flags.None);
}
genericArgs = formatter._outputBuilder.ToString();
formatter._outputBuilder.Clear();
}
formatter.EmitMethodParameters(methodHandle);
string signatureString = formatter._outputBuilder.ToString();
return (owningTypeString, genericArgs, methodName, signatureString);
}
/// <summary>
/// Emit parenthesized method argument type list with parameter names.
/// </summary>
/// <param name="methodHandle">Method handle to use for parameter formatting</param>
private void EmitMethodParameters(MethodHandle methodHandle)
{
bool TryGetNextParameter(ref ParameterHandleCollection.Enumerator enumerator, out Parameter parameter)
{
bool hasNext = enumerator.MoveNext();
parameter = hasNext ? enumerator.Current.GetParameter(_metadataReader) : default;
return hasNext;
}
Method method = methodHandle.GetMethod(_metadataReader);
HandleCollection typeVector = method.Signature.GetMethodSignature(_metadataReader).Parameters;
ParameterHandleCollection.Enumerator parameters = method.Parameters.GetEnumerator();
bool hasParameter = TryGetNextParameter(ref parameters, out Parameter parameter);
if (hasParameter && parameter.Sequence == 0)
{
hasParameter = TryGetNextParameter(ref parameters, out parameter);
}
uint typeIndex = 0;
foreach (Handle type in typeVector)
{
if (typeIndex != 0)
{
_outputBuilder.Append(", ");
}
EmitTypeName(type, Flags.None);
if (++typeIndex == parameter.Sequence && hasParameter)
{
string name = parameter.Name.GetConstantStringValue(_metadataReader).Value;
hasParameter = TryGetNextParameter(ref parameters, out parameter);
if (!string.IsNullOrEmpty(name))
{
_outputBuilder.Append(' ');
_outputBuilder.Append(name);
}
}
}
}
/// <summary>
/// Emit comma-separated list of type names into the output string builder.
/// </summary>
/// <param name="typeVector">Enumeration of type handles to output</param>
private void EmitTypeVector(HandleCollection typeVector)
{
bool first = true;
foreach (Handle handle in typeVector)
{
if (first)
{
first = false;
}
else
{
_outputBuilder.Append(", ");
}
EmitTypeName(handle, Flags.None);
}
}
/// <summary>
/// Emit the name of a given type to the output string builder.
/// </summary>
private void EmitTypeName(Handle typeHandle, Flags flags)
{
switch (typeHandle.HandleType)
{
case HandleType.TypeReference:
EmitTypeReferenceName(typeHandle.ToTypeReferenceHandle(_metadataReader), flags);
break;
case HandleType.TypeSpecification:
EmitTypeSpecificationName(typeHandle.ToTypeSpecificationHandle(_metadataReader), flags);
break;
case HandleType.TypeInstantiationSignature:
EmitTypeInstantiationName(typeHandle.ToTypeInstantiationSignatureHandle(_metadataReader), flags);
break;
case HandleType.SZArraySignature:
EmitSZArrayTypeName(typeHandle.ToSZArraySignatureHandle(_metadataReader), flags);
break;
case HandleType.ArraySignature:
EmitArrayTypeName(typeHandle.ToArraySignatureHandle(_metadataReader), flags);
break;
case HandleType.PointerSignature:
EmitPointerTypeName(typeHandle.ToPointerSignatureHandle(_metadataReader));
break;
case HandleType.ByReferenceSignature:
EmitByRefTypeName(typeHandle.ToByReferenceSignatureHandle(_metadataReader));
break;
case HandleType.TypeDefinition:
EmitTypeDefinitionName(typeHandle.ToTypeDefinitionHandle(_metadataReader), flags);
break;
case HandleType.TypeVariableSignature:
EmitTypeName(_typeContext.GetTypeVariable(typeHandle.ToTypeVariableSignatureHandle(_metadataReader).GetTypeVariableSignature(_metadataReader).Number), flags);
break;
case HandleType.MethodTypeVariableSignature:
EmitTypeName(_typeContext.GetMethodVariable(typeHandle.ToMethodTypeVariableSignatureHandle(_metadataReader).GetMethodTypeVariableSignature(_metadataReader).Number), flags);
break;
case HandleType.GenericParameter:
EmitString(typeHandle.ToGenericParameterHandle(_metadataReader).GetGenericParameter(_metadataReader).Name);
break;
case HandleType.FunctionPointerSignature:
EmitFunctionPointerTypeName();
break;
// This is not an actual type, but we don't always bother representing generic arguments on generic methods as types
case HandleType.ConstantStringValue:
EmitString(typeHandle.ToConstantStringValueHandle(_metadataReader));
break;
default:
Debug.Fail($"Type handle {typeHandle.HandleType} was not handled");
_outputBuilder.Append("???");
break;
}
}
/// <summary>
/// Emit namespace reference.
/// </summary>
/// <param name="namespaceRefHandle">Namespace reference handle</param>
private void EmitNamespaceReferenceName(NamespaceReferenceHandle namespaceRefHandle)
{
NamespaceReference namespaceRef = _metadataReader.GetNamespaceReference(namespaceRefHandle);
if (!namespaceRef.ParentScopeOrNamespace.IsNil &&
namespaceRef.ParentScopeOrNamespace.HandleType == HandleType.NamespaceReference)
{
int charsWritten = _outputBuilder.Length;
EmitNamespaceReferenceName(namespaceRef.ParentScopeOrNamespace.ToNamespaceReferenceHandle(_metadataReader));
if (_outputBuilder.Length - charsWritten > 0)
_outputBuilder.Append('.');
}
EmitString(namespaceRef.Name);
}
private void EmitNamespaceDefinitionName(NamespaceDefinitionHandle namespaceDefHandle)
{
NamespaceDefinition namespaceDef = _metadataReader.GetNamespaceDefinition(namespaceDefHandle);
if (!namespaceDef.ParentScopeOrNamespace.IsNil &&
namespaceDef.ParentScopeOrNamespace.HandleType == HandleType.NamespaceDefinition)
{
int charsWritten = _outputBuilder.Length;
EmitNamespaceDefinitionName(namespaceDef.ParentScopeOrNamespace.ToNamespaceDefinitionHandle(_metadataReader));
if (_outputBuilder.Length - charsWritten > 0)
_outputBuilder.Append('.');
}
EmitString(namespaceDef.Name);
}
/// <summary>
/// Emit type reference.
/// </summary>
private void EmitTypeReferenceName(TypeReferenceHandle typeRefHandle, Flags flags)
{
TypeReference typeRef = _metadataReader.GetTypeReference(typeRefHandle);
if (!typeRef.ParentNamespaceOrType.IsNil)
{
if (typeRef.ParentNamespaceOrType.HandleType != HandleType.NamespaceReference)
{
// Nested type
EmitTypeName(typeRef.ParentNamespaceOrType, flags);
if ((flags & Flags.ReflectionFormat) != 0)
_outputBuilder.Append('+');
else
_outputBuilder.Append('.');
}
else if ((flags & Flags.NamespaceQualify) != 0)
{
int charsWritten = _outputBuilder.Length;
EmitNamespaceReferenceName(typeRef.ParentNamespaceOrType.ToNamespaceReferenceHandle(_metadataReader));
if (_outputBuilder.Length - charsWritten > 0)
_outputBuilder.Append('.');
}
}
EmitString(typeRef.TypeName);
}
private void EmitTypeDefinitionName(TypeDefinitionHandle typeDefHandle, Flags flags)
{
TypeDefinition typeDef = _metadataReader.GetTypeDefinition(typeDefHandle);
if (!typeDef.EnclosingType.IsNil)
{
// Nested type
EmitTypeName(typeDef.EnclosingType, flags);
if ((flags & Flags.ReflectionFormat) != 0)
_outputBuilder.Append('+');
else
_outputBuilder.Append('.');
}
else if ((flags & Flags.NamespaceQualify) != 0)
{
int charsWritten = _outputBuilder.Length;
EmitNamespaceDefinitionName(typeDef.NamespaceDefinition);
if (_outputBuilder.Length - charsWritten > 0)
_outputBuilder.Append('.');
}
EmitString(typeDef.Name);
}
/// <summary>
/// Emit an arbitrary type specification.
/// </summary>
private void EmitTypeSpecificationName(TypeSpecificationHandle typeSpecHandle, Flags flags)
{
TypeSpecification typeSpec = _metadataReader.GetTypeSpecification(typeSpecHandle);
EmitTypeName(typeSpec.Signature, flags);
}
/// <summary>
/// Emit generic instantiation type.
/// </summary>
private void EmitTypeInstantiationName(TypeInstantiationSignatureHandle typeInstHandle, Flags flags)
{
// Stack trace metadata ignores the instantiation arguments of the type in the CLR
TypeInstantiationSignature typeInst = _metadataReader.GetTypeInstantiationSignature(typeInstHandle);
EmitTypeName(typeInst.GenericType, flags);
}
/// <summary>
/// Emit SZArray (single-dimensional array with zero lower bound) type.
/// </summary>
private void EmitSZArrayTypeName(SZArraySignatureHandle szArraySigHandle, Flags flags)
{
SZArraySignature szArraySig = _metadataReader.GetSZArraySignature(szArraySigHandle);
EmitTypeName(szArraySig.ElementType, flags);
_outputBuilder.Append("[]");
}
/// <summary>
/// Emit multi-dimensional array type.
/// </summary>
private void EmitArrayTypeName(ArraySignatureHandle arraySigHandle, Flags flags)
{
ArraySignature arraySig = _metadataReader.GetArraySignature(arraySigHandle);
EmitTypeName(arraySig.ElementType, flags);
_outputBuilder.Append('[');
if (arraySig.Rank > 1)
{
_outputBuilder.Append(',', arraySig.Rank - 1);
}
else
{
_outputBuilder.Append('*');
}
_outputBuilder.Append(']');
}
/// <summary>
/// Emit pointer type.
/// </summary>
/// <param name="pointerSigHandle">Pointer type specification signature handle</param>
private void EmitPointerTypeName(PointerSignatureHandle pointerSigHandle)
{
PointerSignature pointerSig = _metadataReader.GetPointerSignature(pointerSigHandle);
EmitTypeName(pointerSig.Type, Flags.None);
_outputBuilder.Append('*');
}
/// <summary>
/// Emit function pointer type.
/// </summary>
private static void EmitFunctionPointerTypeName()
{
// Function pointer types have no textual representation and we have tests making sure
// they show up as empty strings in stack traces, so deliberately do nothing.
}
/// <summary>
/// Emit by-reference type.
/// </summary>
/// <param name="byRefSigHandle">ByReference type specification signature handle</param>
private void EmitByRefTypeName(ByReferenceSignatureHandle byRefSigHandle)
{
ByReferenceSignature byRefSig = _metadataReader.GetByReferenceSignature(byRefSigHandle);
EmitTypeName(byRefSig.Type, Flags.None);
_outputBuilder.Append('&');
}
/// <summary>
/// Emit a string (represented by a serialized ConstantStringValue) to the output string builder.
/// </summary>
/// <param name="stringHandle">Constant string value token (offset within stack trace native metadata)</param>
private void EmitString(ConstantStringValueHandle stringHandle)
{
_outputBuilder.Append(_metadataReader.GetConstantStringValue(stringHandle).Value);
}
private struct SigTypeContext
{
private readonly object _typeContext;
private readonly object _methodContext;
public SigTypeContext(object typeContext, object methodContext)
{
_typeContext = typeContext;
_methodContext = methodContext;
}
public static Handle GetHandleAt(HandleCollection collection, int index)
{
int currentIndex = 0;
foreach (var currentArg in collection)
{
if (currentIndex == index)
return currentArg;
currentIndex++;
}
Debug.Assert(false);
return default(Handle);
}
public static Handle GetHandleAt(GenericParameterHandleCollection collection, int index)
{
int currentIndex = 0;
foreach (var currentArg in collection)
{
if (currentIndex == index)
return currentArg;
currentIndex++;
}
Debug.Assert(false);
return default(Handle);
}
public Handle GetTypeVariable(int index)
{
return _typeContext is GenericParameterHandleCollection ?
GetHandleAt((GenericParameterHandleCollection)_typeContext, index) :
GetHandleAt((HandleCollection)_typeContext, index);
}
public Handle GetMethodVariable(int index)
{
return _methodContext is GenericParameterHandleCollection ?
GetHandleAt((GenericParameterHandleCollection)_methodContext, index) :
GetHandleAt((HandleCollection)_methodContext, index);
}
private static object GetTypeContext(MetadataReader metadataReader, Handle handle)
{
switch (handle.HandleType)
{
case HandleType.MemberReference:
MemberReference memberRef = handle.ToMemberReferenceHandle(metadataReader).GetMemberReference(metadataReader);
return GetTypeContext(metadataReader, memberRef.Parent);
case HandleType.QualifiedMethod:
QualifiedMethod qualifiedMethod = handle.ToQualifiedMethodHandle(metadataReader).GetQualifiedMethod(metadataReader);
return GetTypeContext(metadataReader, qualifiedMethod.EnclosingType);
case HandleType.TypeDefinition:
TypeDefinition typeDef = handle.ToTypeDefinitionHandle(metadataReader).GetTypeDefinition(metadataReader);
return typeDef.GenericParameters;
case HandleType.TypeReference:
return default(HandleCollection);
case HandleType.TypeSpecification:
TypeSpecification typeSpec = handle.ToTypeSpecificationHandle(metadataReader).GetTypeSpecification(metadataReader);
if (typeSpec.Signature.HandleType != HandleType.TypeInstantiationSignature)
{
Debug.Assert(false);
return default(HandleCollection);
}
return typeSpec.Signature.ToTypeInstantiationSignatureHandle(metadataReader).GetTypeInstantiationSignature(metadataReader).GenericTypeArguments;
default:
Debug.Assert(false);
return default(HandleCollection);
}
}
public static SigTypeContext FromMethod(MetadataReader metadataReader, Handle enclosingTypeHandle, ConstantStringArrayHandle methodInst)
{
object methodContext = null;
if (!methodInst.IsNil)
methodContext = methodInst.GetConstantStringArray(metadataReader).Value;
return new SigTypeContext(GetTypeContext(metadataReader, enclosingTypeHandle), methodContext);
}
public static SigTypeContext FromMethod(MetadataReader metadataReader, TypeDefinitionHandle enclosingTypeHandle, MethodHandle methodHandle)
{
Method method = metadataReader.GetMethod(methodHandle);
return new SigTypeContext(GetTypeContext(metadataReader, enclosingTypeHandle), method.GenericParameters);
}
}
}
}
|