File: SignatureComparer.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// 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.
 
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection.Metadata;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.RuntimeMembers
{
    /// <summary>
    /// Helper class to match signatures in format of 
    /// MemberDescriptor.Signature to members.
    /// </summary>
    internal abstract class SignatureComparer<MethodSymbol, FieldSymbol, PropertySymbol, TypeSymbol, ParameterSymbol>
        where MethodSymbol : class
        where FieldSymbol : class
        where PropertySymbol : class
        where TypeSymbol : class
        where ParameterSymbol : class
    {
        /// <summary>
        /// Returns true if signature matches signature of the field.
        /// Signature should be in format described in MemberDescriptor.
        /// </summary>
        public bool MatchFieldSignature(FieldSymbol field, ImmutableArray<byte> signature)
        {
            int position = 0;
 
            // get the type
            bool result = MatchType(GetFieldType(field), signature, ref position);
 
            Debug.Assert(!result || position == signature.Length);
            return result;
        }
 
        /// <summary>
        /// Returns true if signature matches signature of the property.
        /// Signature should be in format described in MemberDescriptor.
        /// </summary>
        public bool MatchPropertySignature(PropertySymbol property, ImmutableArray<byte> signature)
        {
            int position = 0;
 
            // Get the parameter count
            int paramCount = signature[position++];
 
            // Get all of the parameters.
            ImmutableArray<ParameterSymbol> parameters = GetParameters(property);
 
            if (paramCount != parameters.Length)
            {
                return false;
            }
 
            bool isByRef = IsByRef(signature, ref position);
            if (IsByRefProperty(property) != isByRef)
            {
                return false;
            }
 
            // get the property type
            if (!MatchType(GetPropertyType(property), signature, ref position))
            {
                return false;
            }
 
            // Match parameters
            foreach (ParameterSymbol parameter in parameters)
            {
                if (!MatchParameter(parameter, signature, ref position))
                {
                    return false;
                }
            }
 
            Debug.Assert(position == signature.Length);
            return true;
        }
 
        /// <summary>
        /// Returns true if signature matches signature of the method.
        /// Signature should be in format described in MemberDescriptor.
        /// </summary>
        public bool MatchMethodSignature(MethodSymbol method, ImmutableArray<byte> signature)
        {
            int position = 0;
 
            // Get the parameter count
            int paramCount = signature[position++];
 
            // Get all of the parameters.
            ImmutableArray<ParameterSymbol> parameters = GetParameters(method);
 
            if (paramCount != parameters.Length)
            {
                return false;
            }
 
            bool isByRef = IsByRef(signature, ref position);
 
            if (IsByRefMethod(method) != isByRef)
            {
                return false;
            }
 
            // get the return type
            if (!MatchType(GetReturnType(method), signature, ref position))
            {
                return false;
            }
 
            // Match parameters
            foreach (ParameterSymbol parameter in parameters)
            {
                if (!MatchParameter(parameter, signature, ref position))
                {
                    return false;
                }
            }
 
            Debug.Assert(position == signature.Length);
            return true;
        }
 
        private bool MatchParameter(ParameterSymbol parameter, ImmutableArray<byte> signature, ref int position)
        {
            bool isByRef = IsByRef(signature, ref position);
 
            if (IsByRefParam(parameter) != isByRef)
            {
                return false;
            }
 
            return MatchType(GetParamType(parameter), signature, ref position);
        }
 
        private static bool IsByRef(ImmutableArray<byte> signature, ref int position)
        {
            SignatureTypeCode typeCode = (SignatureTypeCode)signature[position];
 
            if (typeCode == SignatureTypeCode.ByReference)
            {
                position++;
                return true;
            }
            else
            {
                return false;
            }
        }
 
        /// <summary>
        /// Does pretty much the same thing as MetadataDecoder.DecodeType only instead of 
        /// producing a type symbol it compares encoded type to the target.
        /// 
        /// Signature should be in format described in MemberDescriptor.
        /// </summary>
        private bool MatchType(TypeSymbol? type, ImmutableArray<byte> signature, ref int position)
        {
            if (type == null)
            {
                return false;
            }
 
            int paramPosition;
 
            // Get the type.
            SignatureTypeCode typeCode = (SignatureTypeCode)signature[position++];
 
            // Switch on the type.
            switch (typeCode)
            {
                case SignatureTypeCode.TypeHandle:
                    // Ecma-335 spec:
                    // 23.1.16 Element types used in signatures
                    // ...
                    // ELEMENT_TYPE_VALUETYPE 0x11 Followed by TypeDef or TypeRef token
                    // ELEMENT_TYPE_CLASS 0x12 Followed by TypeDef or TypeRef token
                    short expectedType = ReadTypeId(signature, ref position);
                    return MatchTypeToTypeId(type, expectedType);
 
                case SignatureTypeCode.Array:
                    if (!MatchType(GetMDArrayElementType(type), signature, ref position))
                    {
                        return false;
                    }
 
                    int countOfDimensions = signature[position++];
 
                    return MatchArrayRank(type, countOfDimensions);
 
                case SignatureTypeCode.SZArray:
                    return MatchType(GetSZArrayElementType(type), signature, ref position);
 
                case SignatureTypeCode.Pointer:
                    return MatchType(GetPointedToType(type), signature, ref position);
 
                case SignatureTypeCode.GenericTypeParameter:
                    paramPosition = signature[position++];
                    return IsGenericTypeParam(type, paramPosition);
 
                case SignatureTypeCode.GenericMethodParameter:
                    paramPosition = signature[position++];
                    return IsGenericMethodTypeParam(type, paramPosition);
 
                case SignatureTypeCode.GenericTypeInstance:
                    if (!MatchType(GetGenericTypeDefinition(type), signature, ref position))
                    {
                        return false;
                    }
 
                    int argumentCount = signature[position++];
 
                    for (int argumentIndex = 0; argumentIndex < argumentCount; argumentIndex++)
                    {
                        if (!MatchType(GetGenericTypeArgument(type, argumentIndex), signature, ref position))
                        {
                            return false;
                        }
                    }
 
                    return true;
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(typeCode);
            }
        }
 
        /// <summary>
        /// Read a type Id from the signature.
        /// This may consume one or two bytes, and therefore increment the position correspondingly.
        /// </summary>
        private static short ReadTypeId(ImmutableArray<byte> signature, ref int position)
        {
            var firstByte = signature[position++];
            if (firstByte == (byte)WellKnownType.ExtSentinel)
            {
                return (short)(signature[position++] + WellKnownType.ExtSentinel);
            }
            else
            {
                return firstByte;
            }
        }
 
        /// <summary>
        /// Should return null in case of error.
        /// </summary>
        protected abstract TypeSymbol? GetGenericTypeArgument(TypeSymbol type, int argumentIndex);
 
        /// <summary>
        /// Should return null in case of error.
        /// </summary>
        protected abstract TypeSymbol? GetGenericTypeDefinition(TypeSymbol type);
 
        protected abstract bool IsGenericMethodTypeParam(TypeSymbol type, int paramPosition);
 
        protected abstract bool IsGenericTypeParam(TypeSymbol type, int paramPosition);
 
        /// <summary>
        /// Should only accept Pointer types.
        /// Should return null in case of error.
        /// </summary>
        protected abstract TypeSymbol? GetPointedToType(TypeSymbol type);
 
        /// <summary>
        /// Should return null in case of error.
        /// </summary>
        protected abstract TypeSymbol? GetSZArrayElementType(TypeSymbol type);
 
        /// <summary>
        /// Should only accept multi-dimensional arrays.
        /// </summary>
        protected abstract bool MatchArrayRank(TypeSymbol type, int countOfDimensions);
 
        /// <summary>
        /// Should only accept multi-dimensional arrays.
        /// Should return null in case of error.
        /// </summary>
        protected abstract TypeSymbol? GetMDArrayElementType(TypeSymbol type);
 
        protected abstract bool MatchTypeToTypeId(TypeSymbol type, int typeId);
 
        protected abstract TypeSymbol GetReturnType(MethodSymbol method);
        protected abstract ImmutableArray<ParameterSymbol> GetParameters(MethodSymbol method);
 
        protected abstract TypeSymbol GetPropertyType(PropertySymbol property);
        protected abstract ImmutableArray<ParameterSymbol> GetParameters(PropertySymbol property);
 
        protected abstract TypeSymbol GetParamType(ParameterSymbol parameter);
 
        protected abstract bool IsByRefParam(ParameterSymbol parameter);
        protected abstract bool IsByRefMethod(MethodSymbol method);
        protected abstract bool IsByRefProperty(PropertySymbol property);
 
        protected abstract TypeSymbol GetFieldType(FieldSymbol field);
    }
}