/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MemberRefMetadataDecoder.cs
335 строк
14 KB
AlekseyTs
FindMethodSymbolInType - handle the case when a type definition is resolved to a type in a different module. (#62865)
02 авг 2022, 19:49
Не верифицирован
02 авг 2022, 19:49
8fc6ef7
Код
Авторство
О чём код?
// 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 System; using System.Collections.Immutable; using System.Diagnostics; using System.Reflection.Metadata; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE { /// <summary> /// This subclass of MetadataDecoder is specifically for finding /// method symbols corresponding to method MemberRefs. The parent /// implementation is unsuitable because it requires a PEMethodSymbol /// for context when decoding method type parameters and no such /// context is available because it is precisely what we are trying /// to find. Since we know in advance that there will be no context /// and that signatures decoded with this class will only be used /// for comparison (when searching through the methods of a known /// TypeSymbol), we can return indexed type parameters instead. /// </summary> internal sealed class MemberRefMetadataDecoder : MetadataDecoder { /// <summary> /// Type context for resolving generic type parameters. /// </summary> private readonly TypeSymbol _containingType; public MemberRefMetadataDecoder( PEModuleSymbol moduleSymbol, TypeSymbol containingType) : base(moduleSymbol, containingType as PENamedTypeSymbol) { Debug.Assert((object)containingType != null); _containingType = containingType; } /// <summary> /// We know that we'll never have a method context because that's what we're /// trying to find. Instead, just return an indexed type parameter that will /// make comparison easier. /// </summary> /// <param name="position"></param> /// <returns></returns> protected override TypeSymbol GetGenericMethodTypeParamSymbol(int position) { // Note: technically this is a source symbol, but we only care about the position return IndexedTypeParameterSymbol.GetTypeParameter(position); } /// <summary> /// This override can handle non-PE types. /// </summary> protected override TypeSymbol GetGenericTypeParamSymbol(int position) { PENamedTypeSymbol peType = _containingType as PENamedTypeSymbol; if ((object)peType != null) { return base.GetGenericTypeParamSymbol(position); } NamedTypeSymbol namedType = _containingType as NamedTypeSymbol; if ((object)namedType != null) { int cumulativeArity; TypeParameterSymbol typeParameter; GetGenericTypeParameterSymbol(position, namedType, out cumulativeArity, out typeParameter); if ((object)typeParameter != null) { return typeParameter; } else { Debug.Assert(cumulativeArity <= position); return new UnsupportedMetadataTypeSymbol(); // position of type parameter too large } } return new UnsupportedMetadataTypeSymbol(); // associated type does not have type parameters } private static void GetGenericTypeParameterSymbol(int position, NamedTypeSymbol namedType, out int cumulativeArity, out TypeParameterSymbol typeArgument) { cumulativeArity = namedType.Arity; typeArgument = null; int arityOffset = 0; var containingType = namedType.ContainingType; if ((object)containingType != null) { int containingTypeCumulativeArity; GetGenericTypeParameterSymbol(position, containingType, out containingTypeCumulativeArity, out typeArgument); cumulativeArity += containingTypeCumulativeArity; arityOffset = containingTypeCumulativeArity; } if (arityOffset <= position && position < cumulativeArity) { Debug.Assert((object)typeArgument == null); typeArgument = namedType.TypeParameters[position - arityOffset]; } } /// <summary> /// Search through the members of the <see cref="_containingType"/> type symbol to find the method that matches a particular /// signature. /// </summary> /// <param name="memberRefOrMethodDef">A MemberRef or a MethodDef handle that can be used to obtain the name and signature of the method</param> /// <param name="methodsOnly">True to only return a method.</param> /// <returns>The matching method symbol, or null if the inputs do not correspond to a valid method.</returns> internal Symbol FindMember(EntityHandle memberRefOrMethodDef, bool methodsOnly) { try { string memberName; BlobHandle signatureHandle; switch (memberRefOrMethodDef.Kind) { case HandleKind.MemberReference: var memberRef = (MemberReferenceHandle)memberRefOrMethodDef; memberName = Module.GetMemberRefNameOrThrow(memberRef); signatureHandle = Module.GetSignatureOrThrow(memberRef); break; case HandleKind.MethodDefinition: var methodDef = (MethodDefinitionHandle)memberRefOrMethodDef; memberName = Module.GetMethodDefNameOrThrow(methodDef); signatureHandle = Module.GetMethodSignatureOrThrow(methodDef); break; default: throw ExceptionUtilities.UnexpectedValue(memberRefOrMethodDef.Kind); } SignatureHeader signatureHeader; BlobReader signaturePointer = this.DecodeSignatureHeaderOrThrow(signatureHandle, out signatureHeader); switch (signatureHeader.RawValue & SignatureHeader.CallingConventionOrKindMask) { case (byte)SignatureCallingConvention.Default: case (byte)SignatureCallingConvention.VarArgs: int typeParamCount; ParamInfo<TypeSymbol>[] targetParamInfo = this.DecodeSignatureParametersOrThrow(ref signaturePointer, signatureHeader, out typeParamCount); return FindMethodBySignature(_containingType, memberName, signatureHeader, typeParamCount, targetParamInfo); case (byte)SignatureKind.Field: if (methodsOnly) { // skip: return null; } FieldInfo<TypeSymbol> fieldInfo = this.DecodeFieldSignature(ref signaturePointer); return FindFieldBySignature(_containingType, memberName, fieldInfo); default: // error: unexpected calling convention return null; } } catch (BadImageFormatException) { return null; } } private static FieldSymbol FindFieldBySignature(TypeSymbol targetTypeSymbol, string targetMemberName, in FieldInfo<TypeSymbol> fieldInfo) { foreach (Symbol member in targetTypeSymbol.GetMembers(targetMemberName)) { var field = member as FieldSymbol; TypeWithAnnotations fieldType; // Ensure the field symbol matches the { IsByRef, RefCustomModifiers, Type, CustomModifiers } from metadata. if ((object)field != null && (field.RefKind != RefKind.None) == fieldInfo.IsByRef && CustomModifiersMatch(field.RefCustomModifiers, fieldInfo.RefCustomModifiers) && TypeSymbol.Equals((fieldType = field.TypeWithAnnotations).Type, fieldInfo.Type, TypeCompareKind.CLRSignatureCompareOptions) && CustomModifiersMatch(fieldType.CustomModifiers, fieldInfo.CustomModifiers)) { // Behavior in the face of multiple matching signatures is // implementation defined - we'll just pick the first one. return field; } } return null; } private static MethodSymbol FindMethodBySignature(TypeSymbol targetTypeSymbol, string targetMemberName, SignatureHeader targetMemberSignatureHeader, int targetMemberTypeParamCount, ParamInfo<TypeSymbol>[] targetParamInfo) { foreach (Symbol member in targetTypeSymbol.GetMembers(targetMemberName)) { var method = member as MethodSymbol; if ((object)method != null && ((byte)method.CallingConvention == targetMemberSignatureHeader.RawValue) && (targetMemberTypeParamCount == method.Arity) && MethodSymbolMatchesParamInfo(method, targetParamInfo)) { // Behavior in the face of multiple matching signatures is // implementation defined - we'll just pick the first one. return method; } } return null; } private static bool MethodSymbolMatchesParamInfo(MethodSymbol candidateMethod, ParamInfo<TypeSymbol>[] targetParamInfo) { int numParams = targetParamInfo.Length - 1; //don't count return type if (candidateMethod.ParameterCount != numParams) { return false; } // IndexedTypeParameterSymbol is not going to be exposed anywhere, // so we'll cheat and use it here for comparison purposes. TypeMap candidateMethodTypeMap = new TypeMap( candidateMethod.TypeParameters, IndexedTypeParameterSymbol.Take(candidateMethod.Arity), true); if (!ReturnTypesMatch(candidateMethod, candidateMethodTypeMap, ref targetParamInfo[0])) { return false; } for (int i = 0; i < numParams; i++) { if (!ParametersMatch(candidateMethod.Parameters[i], candidateMethodTypeMap, ref targetParamInfo[i + 1 /*for return type*/])) { return false; } } return true; } private static bool ParametersMatch(ParameterSymbol candidateParam, TypeMap candidateMethodTypeMap, ref ParamInfo<TypeSymbol> targetParam) { Debug.Assert(candidateMethodTypeMap != null); // This could be combined into a single return statement with a more complicated expression, but that would // be harder to debug. if ((candidateParam.RefKind != RefKind.None) != targetParam.IsByRef) { return false; } // CONSIDER: Do we want to add special handling for error types? Right now, we expect they'll just fail to match. var substituted = candidateParam.TypeWithAnnotations.SubstituteType(candidateMethodTypeMap); if (!TypeSymbol.Equals(substituted.Type, targetParam.Type, TypeCompareKind.CLRSignatureCompareOptions)) { return false; } if (!CustomModifiersMatch(substituted.CustomModifiers, targetParam.CustomModifiers) || !CustomModifiersMatch(candidateMethodTypeMap.SubstituteCustomModifiers(candidateParam.RefCustomModifiers), targetParam.RefCustomModifiers)) { return false; } return true; } private static bool ReturnTypesMatch(MethodSymbol candidateMethod, TypeMap candidateMethodTypeMap, ref ParamInfo<TypeSymbol> targetReturnParam) { Debug.Assert(candidateMethodTypeMap != null); if (candidateMethod.ReturnsByRef != targetReturnParam.IsByRef) { return false; } TypeWithAnnotations candidateMethodType = candidateMethod.ReturnTypeWithAnnotations; TypeSymbol targetReturnType = targetReturnParam.Type; // CONSIDER: Do we want to add special handling for error types? Right now, we expect they'll just fail to match. var substituted = candidateMethodType.SubstituteType(candidateMethodTypeMap); if (!TypeSymbol.Equals(substituted.Type, targetReturnType, TypeCompareKind.CLRSignatureCompareOptions)) { return false; } if (!CustomModifiersMatch(substituted.CustomModifiers, targetReturnParam.CustomModifiers) || !CustomModifiersMatch(candidateMethodTypeMap.SubstituteCustomModifiers(candidateMethod.RefCustomModifiers), targetReturnParam.RefCustomModifiers)) { return false; } return true; } private static bool CustomModifiersMatch(ImmutableArray<CustomModifier> candidateCustomModifiers, ImmutableArray<ModifierInfo<TypeSymbol>> targetCustomModifiers) { if (targetCustomModifiers.IsDefault || targetCustomModifiers.IsEmpty) { return candidateCustomModifiers.IsDefault || candidateCustomModifiers.IsEmpty; } else if (candidateCustomModifiers.IsDefault) { return false; } var n = candidateCustomModifiers.Length; if (targetCustomModifiers.Length != n) { return false; } for (int i = 0; i < n; i++) { var targetCustomModifier = targetCustomModifiers[i]; CustomModifier candidateCustomModifier = candidateCustomModifiers[i]; if (targetCustomModifier.IsOptional != candidateCustomModifier.IsOptional || !object.Equals(targetCustomModifier.Modifier, ((CSharpCustomModifier)candidateCustomModifier).ModifierSymbol)) { return false; } } return true; } } }