|
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using ILLink.Shared.TypeSystemProxy;
using Mono.Cecil;
namespace Mono.Linker
{
internal static class TypeReferenceExtensions
{
public static string GetDisplayName (this TypeReference type)
{
var builder = GetDisplayNameWithoutNamespace (type);
var namespaceDisplayName = type.GetNamespaceDisplayName ();
if (!string.IsNullOrEmpty (namespaceDisplayName)) {
builder.Insert (0, ".");
builder.Insert (0, namespaceDisplayName);
}
return builder.ToString ();
}
public static StringBuilder GetDisplayNameWithoutNamespace (this TypeReference type)
{
var sb = new StringBuilder ();
if (type == null)
return sb;
Stack<TypeReference>? genericArguments = null;
while (true) {
switch (type) {
case ArrayType arrayType:
AppendArrayType (arrayType, sb);
break;
case GenericInstanceType genericInstanceType:
genericArguments = new Stack<TypeReference> (genericInstanceType.GenericArguments);
type = genericInstanceType.ElementType;
continue;
default:
if (type.HasGenericParameters) {
int genericParametersCount = type.GenericParameters.Count;
int declaringTypeGenericParametersCount = type.DeclaringType?.GenericParameters?.Count ?? 0;
string simpleName;
if (genericParametersCount > declaringTypeGenericParametersCount) {
if (genericArguments?.Count > 0)
PrependGenericArguments (genericArguments, genericParametersCount - declaringTypeGenericParametersCount, sb);
else
PrependGenericParameters (type.GenericParameters.Skip (declaringTypeGenericParametersCount).ToList (), sb);
int explicitArityIndex = type.Name.IndexOf ('`');
simpleName = explicitArityIndex != -1 ? type.Name.Substring (0, explicitArityIndex) : type.Name;
} else
simpleName = type.Name;
sb.Insert (0, simpleName);
break;
}
sb.Insert (0, type.Name);
break;
}
type = type.GetElementType ();
if (type.DeclaringType is not TypeReference declaringType)
break;
type = declaringType;
sb.Insert (0, '.');
}
return sb;
}
internal static void PrependGenericParameters (IList<GenericParameter> genericParameters, StringBuilder sb)
{
sb.Insert (0, '>').Insert (0, genericParameters[genericParameters.Count - 1]);
for (int i = genericParameters.Count - 2; i >= 0; i--)
sb.Insert (0, ',').Insert (0, genericParameters[i]);
sb.Insert (0, '<');
}
static void PrependGenericArguments (Stack<TypeReference> genericArguments, int argumentsToTake, StringBuilder sb)
{
sb.Insert (0, '>').Insert (0, genericArguments.Pop ().GetDisplayNameWithoutNamespace ().ToString ());
while (--argumentsToTake > 0)
sb.Insert (0, ',').Insert (0, genericArguments.Pop ().GetDisplayNameWithoutNamespace ().ToString ());
sb.Insert (0, '<');
}
static void AppendArrayType (ArrayType arrayType, StringBuilder sb)
{
void parseArrayDimensions (ArrayType at)
{
sb.Append ('[');
for (int i = 0; i < at.Dimensions.Count - 1; i++)
sb.Append (',');
sb.Append (']');
}
sb.Append (arrayType.Name.AsSpan (0, arrayType.Name.IndexOf ('[')));
parseArrayDimensions (arrayType);
var element = arrayType.ElementType as ArrayType;
while (element != null) {
parseArrayDimensions (element);
element = element.ElementType as ArrayType;
}
}
public static TypeReference? GetInflatedDeclaringType (this TypeReference type, ITryResolveMetadata resolver)
{
if (type.IsGenericParameter || type.IsByReference || type.IsPointer)
return null;
if (type is SentinelType sentinelType)
return sentinelType.ElementType.GetInflatedDeclaringType (resolver);
if (type is PinnedType pinnedType)
return pinnedType.ElementType.GetInflatedDeclaringType (resolver);
if (type is RequiredModifierType requiredModifierType)
return requiredModifierType.ElementType.GetInflatedDeclaringType (resolver);
if (type is GenericInstanceType genericInstance) {
var declaringType = genericInstance.DeclaringType;
if (declaringType.HasGenericParameters) {
var result = new GenericInstanceType (declaringType);
for (var i = 0; i < declaringType.GenericParameters.Count; ++i)
result.GenericArguments.Add (genericInstance.GenericArguments[i]);
return result;
}
return declaringType;
}
if (type is TypeDefinition typeDefinition)
return typeDefinition.DeclaringType;
Debug.Assert (false);
return null;
}
public static TypeReference InflateFrom (this TypeReference typeToInflate, IGenericInstance? maybeGenericInstanceProvider)
{
if (maybeGenericInstanceProvider is IGenericInstance genericInstanceProvider)
return InflateGenericType (genericInstanceProvider, typeToInflate);
return typeToInflate;
}
public static IEnumerable<(TypeReference InflatedInterface, InterfaceImplementation OriginalImpl)> GetInflatedInterfaces (this TypeReference typeRef, ITryResolveMetadata resolver)
{
var typeDef = resolver.TryResolve (typeRef);
if (typeDef?.HasInterfaces != true)
yield break;
if (typeRef is GenericInstanceType genericInstance) {
foreach (var interfaceImpl in typeDef.Interfaces)
yield return (InflateGenericType (genericInstance, interfaceImpl.InterfaceType), interfaceImpl);
} else {
foreach (var interfaceImpl in typeDef.Interfaces)
yield return (interfaceImpl.InterfaceType, interfaceImpl);
}
}
public static TypeReference InflateGenericType (IGenericInstance genericInstanceProvider, TypeReference typeToInflate)
{
Debug.Assert (genericInstanceProvider is GenericInstanceType or GenericInstanceMethod);
if (typeToInflate is ArrayType arrayType) {
var inflatedElementType = InflateGenericType (genericInstanceProvider, arrayType.ElementType);
if (inflatedElementType != arrayType.ElementType)
return new ArrayType (inflatedElementType, arrayType.Rank);
return arrayType;
}
if (typeToInflate is GenericInstanceType genericInst)
return MakeGenericType (genericInstanceProvider, genericInst);
if (typeToInflate is GenericParameter genericParameter) {
if (genericParameter.Owner is MethodReference) {
if (genericInstanceProvider is not GenericInstanceMethod)
return typeToInflate;
return genericInstanceProvider.GenericArguments[genericParameter.Position];
}
Debug.Assert (genericParameter.Owner is TypeReference);
if (genericInstanceProvider is not GenericInstanceType) {
if (((GenericInstanceMethod) genericInstanceProvider).DeclaringType is not GenericInstanceType genericInstanceType)
return typeToInflate;
genericInstanceProvider = genericInstanceType;
}
return genericInstanceProvider.GenericArguments[genericParameter.Position];
}
if (typeToInflate is FunctionPointerType functionPointerType) {
var result = new FunctionPointerType {
ReturnType = InflateGenericType (genericInstanceProvider, functionPointerType.ReturnType)
};
for (int i = 0; i < functionPointerType.Parameters.Count; i++) {
var inflatedParameterType = InflateGenericType (genericInstanceProvider, functionPointerType.Parameters[i].ParameterType);
result.Parameters.Add (new ParameterDefinition (inflatedParameterType));
}
return result;
}
if (typeToInflate is IModifierType modifierType) {
var modifier = InflateGenericType (genericInstanceProvider, modifierType.ModifierType);
var elementType = InflateGenericType (genericInstanceProvider, modifierType.ElementType);
if (modifierType is OptionalModifierType) {
return new OptionalModifierType (modifier, elementType);
}
return new RequiredModifierType (modifier, elementType);
}
if (typeToInflate is PinnedType pinnedType) {
var elementType = InflateGenericType (genericInstanceProvider, pinnedType.ElementType);
if (elementType != pinnedType.ElementType)
return new PinnedType (elementType);
return pinnedType;
}
if (typeToInflate is PointerType pointerType) {
var elementType = InflateGenericType (genericInstanceProvider, pointerType.ElementType);
if (elementType != pointerType.ElementType)
return new PointerType (elementType);
return pointerType;
}
if (typeToInflate is ByReferenceType byReferenceType) {
var elementType = InflateGenericType (genericInstanceProvider, byReferenceType.ElementType);
if (elementType != byReferenceType.ElementType)
return new ByReferenceType (elementType);
return byReferenceType;
}
if (typeToInflate is SentinelType sentinelType) {
var elementType = InflateGenericType (genericInstanceProvider, sentinelType.ElementType);
if (elementType != sentinelType.ElementType)
return new SentinelType (elementType);
return sentinelType;
}
return typeToInflate;
}
private static GenericInstanceType MakeGenericType (IGenericInstance genericInstanceProvider, GenericInstanceType type)
{
var result = new GenericInstanceType (type.ElementType);
for (var i = 0; i < type.GenericArguments.Count; ++i) {
result.GenericArguments.Add (InflateGenericType (genericInstanceProvider, type.GenericArguments[i]));
}
return result;
}
public static IEnumerable<MethodReference> GetMethods (this TypeReference type, ITryResolveMetadata resolver)
{
TypeDefinition? typeDef = resolver.TryResolve (type);
if (typeDef?.HasMethods != true)
yield break;
if (type is GenericInstanceType genericInstanceType) {
foreach (var methodDef in typeDef.Methods)
yield return MakeMethodReferenceForGenericInstanceType (genericInstanceType, methodDef);
} else {
foreach (var method in typeDef.Methods)
yield return method;
}
}
private static MethodReference MakeMethodReferenceForGenericInstanceType (GenericInstanceType genericInstanceType, MethodDefinition methodDef)
{
var method = new MethodReference (methodDef.Name, methodDef.ReturnType, genericInstanceType) {
HasThis = methodDef.HasThis,
ExplicitThis = methodDef.ExplicitThis,
CallingConvention = methodDef.CallingConvention
};
#pragma warning disable RS0030 // MethodReference.Parameters is banned. It makes sense to use when needing to directly use Cecil's api.
foreach (var parameter in methodDef.Parameters)
method.Parameters.Add (new ParameterDefinition (parameter.Name, parameter.Attributes, parameter.ParameterType));
#pragma warning restore RS0030
foreach (var gp in methodDef.GenericParameters)
method.GenericParameters.Add (new GenericParameter (gp.Name, method));
return method;
}
public static string ToCecilName (this string fullTypeName)
{
return fullTypeName.Replace ('+', '/');
}
public static bool HasDefaultConstructor (this TypeDefinition type, LinkContext context)
{
foreach (var m in type.Methods) {
if (m.HasMetadataParameters ())
continue;
var definition = context.Resolve (m);
if (definition?.IsDefaultConstructor () == true)
return true;
}
return false;
}
public static MethodReference GetDefaultInstanceConstructor (this TypeDefinition type, LinkContext context)
{
foreach (var m in type.Methods) {
if (m.HasMetadataParameters ())
continue;
var definition = context.Resolve (m);
if (definition?.IsDefaultConstructor () != true)
continue;
return m;
}
throw new NotImplementedException ();
}
public static bool IsTypeOf (this TypeReference type, string ns, string name)
{
return type.Name == name
&& type.Namespace == ns;
}
public static bool IsTypeOf (this TypeReference type, string fullTypeName)
{
var name = fullTypeName.AsSpan ();
if (type.Name.Length + 1 > name.Length)
return false;
if (!name.Slice (name.Length - type.Name.Length).Equals (type.Name.AsSpan (), StringComparison.Ordinal))
return false;
if (name[name.Length - type.Name.Length - 1] != '.')
return false;
return name.Slice (0, name.Length - type.Name.Length - 1).Equals (type.Namespace, StringComparison.Ordinal);
}
public static bool IsTypeOf<T> (this TypeReference tr)
{
var type = typeof (T);
return tr.Name == type.Name && tr.Namespace == type.Namespace;
}
public static bool IsTypeOf (this TypeReference tr, WellKnownType type)
{
return tr.TryGetWellKnownType () == type;
}
public static WellKnownType? TryGetWellKnownType (this TypeReference tr)
{
return tr.MetadataType switch {
MetadataType.String => WellKnownType.System_String,
MetadataType.Object => WellKnownType.System_Object,
MetadataType.Void => WellKnownType.System_Void,
// TypeReferences of System.Array do not have a MetadataType of MetadataType.Array -- use string checking instead
MetadataType.Array or _ => WellKnownTypeExtensions.GetWellKnownType (tr.Namespace, tr.Name)
};
}
public static bool IsSubclassOf (this TypeReference type, string ns, string name, ITryResolveMetadata resolver)
{
TypeDefinition? baseType = resolver.TryResolve (type);
while (baseType != null) {
if (baseType.IsTypeOf (ns, name))
return true;
baseType = resolver.TryResolve (baseType.BaseType);
}
return false;
}
public static TypeReference WithoutModifiers (this TypeReference type)
{
while (type is IModifierType) {
type = ((IModifierType) type).ElementType;
}
return type;
}
// Check whether this type represents a "named type" (i.e. a type that has a name and can be resolved to a TypeDefinition),
// not an array, pointer, byref, or generic parameter. Conceptually this is supposed to represent the same idea as Roslyn's
// INamedTypeSymbol, or ILC's DefType/MetadataType.
public static bool IsNamedType (this TypeReference typeReference) {
if (typeReference.IsRequiredModifier)
return ((RequiredModifierType) typeReference).ElementType.IsNamedType ();
if (typeReference.IsOptionalModifier)
return ((OptionalModifierType) typeReference).ElementType.IsNamedType ();
if (typeReference.IsDefinition || typeReference.IsGenericInstance)
return true;
if (typeReference.IsArray ||
typeReference.IsByReference ||
typeReference.IsPointer ||
typeReference.IsFunctionPointer ||
typeReference.IsGenericParameter)
return false;
// Shouldn't get called for these cases
Debug.Assert (!typeReference.IsPinned);
Debug.Assert (!typeReference.IsSentinel);
if (typeReference.IsPinned || typeReference.IsSentinel)
return false;
Debug.Assert (typeReference.GetType () == typeof (TypeReference));
return true;
}
/// <summary>
/// Resolves a TypeReference to a TypeDefinition if possible. Non-named types other than arrays (pointers, byrefs, function pointers) return null.
/// Array types that are dynamically accessed resolve to System.Array instead of its element type - which is what Cecil resolves to.
/// Any data flow annotations placed on a type parameter which receives an array type apply to the array itself. None of the members in its
/// element type should be marked.
/// </summary>
public static TypeDefinition? ResolveToTypeDefinition (this TypeReference typeReference, LinkContext context)
=> typeReference is ArrayType
? BCL.FindPredefinedType (WellKnownType.System_Array, context)
: typeReference.IsNamedType ()
? context.TryResolve (typeReference)
: null;
public static bool IsByRefOrPointer (this TypeReference typeReference)
{
return typeReference.WithoutModifiers ().MetadataType switch {
MetadataType.Pointer or MetadataType.ByReference => true,
_ => false,
};
}
}
}
|