File: Linker\MethodReferenceExtensions.cs
Web Access
Project: src\src\tools\illink\src\linker\Mono.Linker.csproj (illink)
// 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 ILLink.Shared.TypeSystemProxy;
using Mono.Cecil;
 
namespace Mono.Linker
{
	public static class MethodReferenceExtensions
	{
		public static string GetDisplayName (this MethodReference method)
		{
			var sb = new System.Text.StringBuilder ();
 
			// Match C# syntaxis name if setter or getter
#pragma warning disable RS0030 // Cecil's Resolve is banned -- this should be a very cold path and makes calling this method much simpler
			var methodDefinition = method.Resolve ();
#pragma warning restore RS0030
			if (methodDefinition != null && (methodDefinition.IsSetter || methodDefinition.IsGetter)) {
				// Append property name
				string name = methodDefinition.IsSetter ? string.Concat (methodDefinition.Name.AsSpan (4), ".set") : string.Concat (methodDefinition.Name.AsSpan (4), ".get");
				sb.Append (name);
				// Insert declaring type name and namespace
				sb.Insert (0, '.').Insert (0, method.DeclaringType.GetDisplayName ());
				return sb.ToString ();
			}
 
			if (methodDefinition != null && methodDefinition.IsEventMethod ()) {
				// Append event name
				string name = methodDefinition.SemanticsAttributes switch {
					MethodSemanticsAttributes.AddOn => string.Concat (methodDefinition.Name.AsSpan (4), ".add"),
					MethodSemanticsAttributes.RemoveOn => string.Concat (methodDefinition.Name.AsSpan (7), ".remove"),
					MethodSemanticsAttributes.Fire => string.Concat (methodDefinition.Name.AsSpan (6), ".raise"),
					_ => throw new NotSupportedException (),
				};
				sb.Append (name);
				// Insert declaring type name and namespace
				sb.Insert (0, '.').Insert (0, method.DeclaringType.GetDisplayName ());
				return sb.ToString ();
			}
 
			// Append parameters
			sb.Append ('(');
			if (method.HasMetadataParameters ()) {
#pragma warning disable RS0030 // MethodReference.Parameters is banned -- it's best to leave this as is for now
				for (int i = 0; i < method.Parameters.Count - 1; i++)
					sb.Append (method.Parameters[i].ParameterType.GetDisplayNameWithoutNamespace ()).Append (", ");
				sb.Append (method.Parameters[method.Parameters.Count - 1].ParameterType.GetDisplayNameWithoutNamespace ());
#pragma warning restore RS0030 // Do not used banned APIs
			}
 
			sb.Append (')');
 
			// Insert generic parameters
			if (method.HasGenericParameters) {
				TypeReferenceExtensions.PrependGenericParameters (method.GenericParameters, sb);
			}
 
			// Insert method name
			if (method.Name == ".ctor")
				sb.Insert (0, method.DeclaringType.Name);
			else
				sb.Insert (0, method.Name);
 
			// Insert declaring type name and namespace
			if (method.DeclaringType != null)
				sb.Insert (0, '.').Insert (0, method.DeclaringType.GetDisplayName ());
 
			return sb.ToString ();
		}
 
		public static TypeReference GetReturnType (this MethodReference method)
		{
			if (method.DeclaringType is GenericInstanceType genericInstance)
				return TypeReferenceExtensions.InflateGenericType (genericInstance, method.ReturnType);
 
			return method.ReturnType;
		}
 
		public static bool ReturnsVoid (this IMethodSignature method)
		{
			return method.ReturnType.WithoutModifiers ().MetadataType == MetadataType.Void;
		}
 
		public static TypeReference GetInflatedParameterType (this MethodReference method, int parameterIndex)
		{
#pragma warning disable RS0030 // MethodReference.Parameters is banned -- it's best to leave this as is for now
			IGenericInstance? genericInstance = method as IGenericInstance ?? method.DeclaringType as IGenericInstance;
			if (genericInstance is null)
				return method.Parameters[parameterIndex].ParameterType;
			return TypeReferenceExtensions.InflateGenericType (genericInstance, method.Parameters[parameterIndex].ParameterType);
#pragma warning restore RS0030 // Do not used banned APIs
		}
 
		/// <summary>
		/// Gets the number of entries in the 'Parameters' section of a method's metadata (i.e. excludes the implicit 'this' from the count)
		/// </summary>
#pragma warning disable RS0030 // MethodReference.Parameters is banned -- this provides a wrapper
		public static int GetMetadataParametersCount (this MethodReference method)
			=> method.Parameters.Count;
#pragma warning restore RS0030 // Do not used banned APIs
 
		/// <summary>
		/// Returns true if the method has any parameters in the .parameters section of the method's metadata (i.e. excludes the impicit 'this')
		/// </summary>
		public static bool HasMetadataParameters (this MethodReference method)
			=> method.GetMetadataParametersCount () != 0;
 
		/// <summary>
		/// Returns the number of the parameters pushed before the method's call (i.e. including the implicit 'this' if present)
		/// </summary>
#pragma warning disable RS0030 // MethodReference.Parameters is banned -- this provides a wrapper
		public static int GetParametersCount (this MethodReference method)
			=> method.Parameters.Count + (method.HasImplicitThis () ? 1 : 0);
#pragma warning restore RS0030 // Do not used banned APIs
 
		public static bool IsDeclaredOnType (this MethodReference method, string fullTypeName)
		{
			return method.DeclaringType.IsTypeOf (fullTypeName);
		}
 
		public static bool HasImplicitThis (this MethodReference method)
		{
			return method.HasThis && !method.ExplicitThis;
		}
 
		/// <summary>
		/// Returns an IEnumerable of the ReferenceKind of each parameter, with the first being for the implicit 'this' parameter if it exists
		/// Used for better performance when it's only necessary to get the ReferenceKind of all parameters and nothing else.
		/// </summary>
		public static IEnumerable<ReferenceKind> GetParameterReferenceKinds (this MethodReference method)
		{
			if (method.HasImplicitThis ())
				yield return method.DeclaringType.IsValueType ? ReferenceKind.Ref : ReferenceKind.None;
#pragma warning disable RS0030 // MethodReference.Parameters is banned -- this provides a wrapper
			foreach (var parameter in method.Parameters)
				yield return GetReferenceKind (parameter);
#pragma warning restore RS0030 // Do not used banned APIs
 
			static ReferenceKind GetReferenceKind (ParameterDefinition param)
			{
				if (!param.ParameterType.IsByReference)
					return ReferenceKind.None;
				if (param.IsIn)
					return ReferenceKind.In;
				if (param.IsOut)
					return ReferenceKind.Out;
				return ReferenceKind.Ref;
			}
		}
	}
}