|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using System.Text;
using System.Diagnostics;
using System.Linq;
namespace Microsoft.Cci
{
internal static class TypeNameSerializer
{
internal static string GetSerializedTypeName(this ITypeReference typeReference, EmitContext context)
{
bool isAssemblyQualified = true;
return GetSerializedTypeName(typeReference, context, ref isAssemblyQualified);
}
internal static string GetSerializedTypeName(this ITypeReference typeReference, EmitContext context, ref bool isAssemblyQualified)
{
var pooled = PooledStringBuilder.GetInstance();
StringBuilder sb = pooled.Builder;
IArrayTypeReference arrType = typeReference as IArrayTypeReference;
if (arrType != null)
{
typeReference = arrType.GetElementType(context);
bool isAssemQual = false;
AppendSerializedTypeName(sb, typeReference, ref isAssemQual, context);
if (arrType.IsSZArray)
{
sb.Append("[]");
}
else
{
sb.Append('[');
if (arrType.Rank == 1)
{
sb.Append('*');
}
sb.Append(',', (int)arrType.Rank - 1);
sb.Append(']');
}
goto done;
}
IPointerTypeReference pointer = typeReference as IPointerTypeReference;
if (pointer != null)
{
typeReference = pointer.GetTargetType(context);
bool isAssemQual = false;
AppendSerializedTypeName(sb, typeReference, ref isAssemQual, context);
sb.Append('*');
goto done;
}
INamespaceTypeReference namespaceType = typeReference.AsNamespaceTypeReference;
if (namespaceType != null)
{
var name = namespaceType.NamespaceName;
if (name.Length != 0)
{
sb.Append(name);
sb.Append('.');
}
sb.Append(GetEscapedMetadataName(namespaceType));
goto done;
}
if (typeReference.IsTypeSpecification())
{
if (typeReference is IFunctionPointerTypeReference)
{
var messageProvider = context.Module.CommonCompilation.MessageProvider;
context.Diagnostics.Add(messageProvider.CreateDiagnostic(
messageProvider.ERR_FunctionPointerTypesInAttributeNotSupported,
context.Location ?? Location.None));
sb.Append("(fnptr)");
goto done;
}
ITypeReference uninstantiatedTypeReference = typeReference.GetUninstantiatedGenericType(context);
Debug.Assert(uninstantiatedTypeReference != typeReference);
ArrayBuilder<ITypeReference> consolidatedTypeArguments = ArrayBuilder<ITypeReference>.GetInstance();
typeReference.GetConsolidatedTypeArguments(consolidatedTypeArguments, context);
bool uninstantiatedTypeIsAssemblyQualified = false;
sb.Append(GetSerializedTypeName(uninstantiatedTypeReference, context, ref uninstantiatedTypeIsAssemblyQualified));
sb.Append('[');
bool first = true;
foreach (ITypeReference argument in consolidatedTypeArguments)
{
if (first)
{
first = false;
}
else
{
sb.Append(',');
}
bool isAssemQual = true;
AppendSerializedTypeName(sb, argument, ref isAssemQual, context);
}
consolidatedTypeArguments.Free();
sb.Append(']');
goto done;
}
INestedTypeReference nestedType = typeReference.AsNestedTypeReference;
if (nestedType != null)
{
bool nestedTypeIsAssemblyQualified = false;
sb.Append(GetSerializedTypeName(nestedType.GetContainingType(context), context, ref nestedTypeIsAssemblyQualified));
sb.Append('+');
sb.Append(GetEscapedMetadataName(nestedType));
goto done;
}
// TODO: error
done:
if (isAssemblyQualified)
{
AppendAssemblyQualifierIfNecessary(sb, UnwrapTypeReference(typeReference, context), out isAssemblyQualified, context);
}
return pooled.ToStringAndFree();
}
private static void AppendSerializedTypeName(StringBuilder sb, ITypeReference type, ref bool isAssemQualified, EmitContext context)
{
string argTypeName = GetSerializedTypeName(type, context, ref isAssemQualified);
if (isAssemQualified)
{
sb.Append('[');
}
sb.Append(argTypeName);
if (isAssemQualified)
{
sb.Append(']');
}
}
private static void AppendAssemblyQualifierIfNecessary(StringBuilder sb, ITypeReference typeReference, out bool isAssemQualified, EmitContext context)
{
INestedTypeReference nestedType = typeReference.AsNestedTypeReference;
if (nestedType != null)
{
AppendAssemblyQualifierIfNecessary(sb, nestedType.GetContainingType(context), out isAssemQualified, context);
return;
}
IGenericTypeInstanceReference genInst = typeReference.AsGenericTypeInstanceReference;
if (genInst != null)
{
AppendAssemblyQualifierIfNecessary(sb, genInst.GetGenericType(context), out isAssemQualified, context);
return;
}
IArrayTypeReference arrType = typeReference as IArrayTypeReference;
if (arrType != null)
{
AppendAssemblyQualifierIfNecessary(sb, arrType.GetElementType(context), out isAssemQualified, context);
return;
}
IPointerTypeReference pointer = typeReference as IPointerTypeReference;
if (pointer != null)
{
AppendAssemblyQualifierIfNecessary(sb, pointer.GetTargetType(context), out isAssemQualified, context);
return;
}
isAssemQualified = false;
IAssemblyReference referencedAssembly = null;
INamespaceTypeReference namespaceType = typeReference.AsNamespaceTypeReference;
if (namespaceType != null)
{
referencedAssembly = namespaceType.GetUnit(context) as IAssemblyReference;
}
if (referencedAssembly != null)
{
var containingAssembly = context.Module.GetContainingAssembly(context);
if (containingAssembly == null || !ReferenceEquals(referencedAssembly, containingAssembly))
{
sb.Append(", ");
sb.Append(MetadataWriter.StrongName(referencedAssembly));
isAssemQualified = true;
}
}
}
private static string GetEscapedMetadataName(INamedTypeReference namedType)
{
var pooled = PooledStringBuilder.GetInstance();
StringBuilder mangledName = pooled.Builder;
const string needsEscaping = "\\[]*.+,& ";
if (namedType.AssociatedFileIdentifier is string fileIdentifier)
{
Debug.Assert(needsEscaping.All(c => !fileIdentifier.Contains(c)));
mangledName.Append(fileIdentifier);
}
foreach (var ch in namedType.Name)
{
if (needsEscaping.IndexOf(ch) >= 0)
{
mangledName.Append('\\');
}
mangledName.Append(ch);
}
if (namedType.MangleName && namedType.GenericParameterCount > 0)
{
mangledName.Append(MetadataHelpers.GetAritySuffix(namedType.GenericParameterCount));
}
return pooled.ToStringAndFree();
}
/// <summary>
/// Strip off *, &, and [].
/// </summary>
private static ITypeReference UnwrapTypeReference(ITypeReference typeReference, EmitContext context)
{
while (true)
{
IArrayTypeReference arrType = typeReference as IArrayTypeReference;
if (arrType != null)
{
typeReference = arrType.GetElementType(context);
continue;
}
IPointerTypeReference pointer = typeReference as IPointerTypeReference;
if (pointer != null)
{
typeReference = pointer.GetTargetType(context);
continue;
}
return typeReference;
}
}
/// <summary>
/// Qualified name of namespace.
/// e.g. "A.B.C"
/// </summary>
internal static string BuildQualifiedNamespaceName(INamespace @namespace)
{
Debug.Assert(@namespace != null);
if (@namespace.ContainingNamespace == null)
{
return @namespace.Name;
}
var namesReversed = ArrayBuilder<string>.GetInstance();
do
{
string name = @namespace.Name;
if (name.Length != 0)
{
namesReversed.Add(name);
}
@namespace = @namespace.ContainingNamespace;
}
while (@namespace != null);
var result = PooledStringBuilder.GetInstance();
for (int i = namesReversed.Count - 1; i >= 0; i--)
{
result.Builder.Append(namesReversed[i]);
if (i > 0)
{
result.Builder.Append('.');
}
}
namesReversed.Free();
return result.ToStringAndFree();
}
}
}
|