/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Portable/Symbols/Metadata/PE/MetadataDecoder.cs
613 строк
23 KB
AlekseyTs
Reduce amount of allocated MissingMetadataTypeSymbol symbols during various lookup operations. (#65046)
04 ноя 2022, 19:07
Не верифицирован
04 ноя 2022, 19:07
93902cc
Код
Авторство
О чём код?
// 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.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Reflection.Metadata; using Microsoft.CodeAnalysis.ErrorReporting; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE { /// <summary> /// Helper class to resolve metadata tokens and signatures. /// </summary> internal class MetadataDecoder : MetadataDecoder<PEModuleSymbol, TypeSymbol, MethodSymbol, FieldSymbol, Symbol> { /// <summary> /// Type context for resolving generic type arguments. /// </summary> private readonly PENamedTypeSymbol _typeContextOpt; /// <summary> /// Method context for resolving generic method type arguments. /// </summary> private readonly PEMethodSymbol _methodContextOpt; public MetadataDecoder( PEModuleSymbol moduleSymbol, PENamedTypeSymbol context) : this(moduleSymbol, context, null) { } public MetadataDecoder( PEModuleSymbol moduleSymbol, PEMethodSymbol context) : this(moduleSymbol, (PENamedTypeSymbol)context.ContainingType, context) { } public MetadataDecoder( PEModuleSymbol moduleSymbol) : this(moduleSymbol, null, null) { } private MetadataDecoder(PEModuleSymbol moduleSymbol, PENamedTypeSymbol typeContextOpt, PEMethodSymbol methodContextOpt) // TODO (tomat): if the containing assembly is a source assembly and we are about to decode assembly level attributes, we run into a cycle, // so for now ignore the assembly identity. : base(moduleSymbol.Module, (moduleSymbol.ContainingAssembly is PEAssemblySymbol) ? moduleSymbol.ContainingAssembly.Identity : null, SymbolFactory.Instance, moduleSymbol) { Debug.Assert((object)moduleSymbol != null); _typeContextOpt = typeContextOpt; _methodContextOpt = methodContextOpt; } internal PEModuleSymbol ModuleSymbol { get { return moduleSymbol; } } protected override TypeSymbol GetGenericMethodTypeParamSymbol(int position) { if ((object)_methodContextOpt == null) { return new UnsupportedMetadataTypeSymbol(); // type parameter not associated with a method } var typeParameters = _methodContextOpt.TypeParameters; if (typeParameters.Length <= position) { return new UnsupportedMetadataTypeSymbol(); // type parameter position too large } return typeParameters[position]; } protected override TypeSymbol GetGenericTypeParamSymbol(int position) { PENamedTypeSymbol type = _typeContextOpt; while ((object)type != null && (type.MetadataArity - type.Arity) > position) { type = type.ContainingSymbol as PENamedTypeSymbol; } if ((object)type == null || type.MetadataArity <= position) { return new UnsupportedMetadataTypeSymbol(); // position of type parameter too large } position -= type.MetadataArity - type.Arity; Debug.Assert(position >= 0 && position < type.Arity); return type.TypeParameters[position]; } protected override ConcurrentDictionary<TypeDefinitionHandle, TypeSymbol> GetTypeHandleToTypeMap() { return moduleSymbol.TypeHandleToTypeMap; } protected override ConcurrentDictionary<TypeReferenceHandle, TypeSymbol> GetTypeRefHandleToTypeMap() { return moduleSymbol.TypeRefHandleToTypeMap; } #nullable enable protected override TypeSymbol LookupNestedTypeDefSymbol(TypeSymbol container, ref MetadataTypeName emittedName) { var result = container.LookupMetadataType(ref emittedName); Debug.Assert(result?.IsErrorType() != true); return result ?? new MissingMetadataTypeSymbol.Nested((NamedTypeSymbol)container, ref emittedName); } /// <summary> /// Lookup a type defined in referenced assembly. /// </summary> /// <param name="referencedAssemblyIndex"></param> /// <param name="emittedName"></param> protected override TypeSymbol LookupTopLevelTypeDefSymbol( int referencedAssemblyIndex, ref MetadataTypeName emittedName) { var assembly = moduleSymbol.GetReferencedAssemblySymbol(referencedAssemblyIndex); if ((object)assembly == null) { return new UnsupportedMetadataTypeSymbol(); } try { return assembly.LookupDeclaredOrForwardedTopLevelMetadataType(ref emittedName, visitedAssemblies: null); } catch (Exception e) when (FatalError.ReportAndPropagate(e)) // Trying to get more useful Watson dumps. { throw ExceptionUtilities.Unreachable(); } } /// <summary> /// Lookup a type defined in a module of a multi-module assembly. /// </summary> protected override TypeSymbol LookupTopLevelTypeDefSymbol(string moduleName, ref MetadataTypeName emittedName, out bool isNoPiaLocalType) { foreach (ModuleSymbol m in moduleSymbol.ContainingAssembly.Modules) { if (string.Equals(m.Name, moduleName, StringComparison.OrdinalIgnoreCase)) { if ((object)m == (object)moduleSymbol) { return moduleSymbol.LookupTopLevelMetadataTypeWithNoPiaLocalTypeUnification(ref emittedName, out isNoPiaLocalType); } else { isNoPiaLocalType = false; var result = m.LookupTopLevelMetadataType(ref emittedName); Debug.Assert(result?.IsErrorType() != true); return result ?? new MissingMetadataTypeSymbol.TopLevel(m, ref emittedName); } } } isNoPiaLocalType = false; return new MissingMetadataTypeSymbol.TopLevel(new MissingModuleSymbolWithName(moduleSymbol.ContainingAssembly, moduleName), ref emittedName, SpecialType.None); } /// <summary> /// Lookup a type defined in this module. /// This method will be called only if the type we are /// looking for hasn't been loaded yet. Otherwise, MetadataDecoder /// would have found the type in TypeDefRowIdToTypeMap based on its /// TypeDef row id. /// </summary> protected override TypeSymbol LookupTopLevelTypeDefSymbol(ref MetadataTypeName emittedName, out bool isNoPiaLocalType) { return moduleSymbol.LookupTopLevelMetadataTypeWithNoPiaLocalTypeUnification(ref emittedName, out isNoPiaLocalType); } #nullable disable protected override int GetIndexOfReferencedAssembly(AssemblyIdentity identity) { // Go through all assemblies referenced by the current module and // find the one which *exactly* matches the given identity. // No unification will be performed var assemblies = this.moduleSymbol.GetReferencedAssemblies(); for (int i = 0; i < assemblies.Length; i++) { if (identity.Equals(assemblies[i])) { return i; } } return -1; } /// <summary> /// Perform a check whether the type or at least one of its generic arguments /// is defined in the specified assemblies. The check is performed recursively. /// </summary> public static bool IsOrClosedOverATypeFromAssemblies(TypeSymbol symbol, ImmutableArray<AssemblySymbol> assemblies) { switch (symbol.Kind) { case SymbolKind.TypeParameter: return false; case SymbolKind.ArrayType: return IsOrClosedOverATypeFromAssemblies(((ArrayTypeSymbol)symbol).ElementType, assemblies); case SymbolKind.PointerType: return IsOrClosedOverATypeFromAssemblies(((PointerTypeSymbol)symbol).PointedAtType, assemblies); case SymbolKind.DynamicType: return false; case SymbolKind.ErrorType: goto case SymbolKind.NamedType; case SymbolKind.NamedType: var namedType = (NamedTypeSymbol)symbol; AssemblySymbol containingAssembly = symbol.OriginalDefinition.ContainingAssembly; int i; if ((object)containingAssembly != null) { for (i = 0; i < assemblies.Length; i++) { if (ReferenceEquals(containingAssembly, assemblies[i])) { return true; } } } do { var arguments = namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; int count = arguments.Length; for (i = 0; i < count; i++) { if (IsOrClosedOverATypeFromAssemblies(arguments[i].Type, assemblies)) { return true; } } namedType = namedType.ContainingType; } while ((object)namedType != null); return false; default: throw ExceptionUtilities.UnexpectedValue(symbol.Kind); } } protected override TypeSymbol SubstituteNoPiaLocalType( TypeDefinitionHandle typeDef, ref MetadataTypeName name, string interfaceGuid, string scope, string identifier) { TypeSymbol result; try { bool isInterface = Module.IsInterfaceOrThrow(typeDef); TypeSymbol baseType = null; if (!isInterface) { EntityHandle baseToken = Module.GetBaseTypeOfTypeOrThrow(typeDef); if (!baseToken.IsNil) { baseType = GetTypeOfToken(baseToken); } } result = SubstituteNoPiaLocalType( ref name, isInterface, baseType, interfaceGuid, scope, identifier, moduleSymbol.ContainingAssembly); } catch (BadImageFormatException mrEx) { result = GetUnsupportedMetadataTypeSymbol(mrEx); } Debug.Assert((object)result != null); ConcurrentDictionary<TypeDefinitionHandle, TypeSymbol> cache = GetTypeHandleToTypeMap(); Debug.Assert(cache != null); TypeSymbol newresult = cache.GetOrAdd(typeDef, result); Debug.Assert(ReferenceEquals(newresult, result) || (newresult.Kind == SymbolKind.ErrorType)); return newresult; } #nullable enable /// <summary> /// Find canonical type for NoPia embedded type. /// </summary> /// <returns> /// Symbol for the canonical type or an ErrorTypeSymbol. Never returns null. /// </returns> internal static NamedTypeSymbol SubstituteNoPiaLocalType( ref MetadataTypeName name, bool isInterface, TypeSymbol baseType, string? interfaceGuid, string? scope, string? identifier, AssemblySymbol referringAssembly) { NamedTypeSymbol? result = null; Guid interfaceGuidValue = new Guid(); bool haveInterfaceGuidValue = false; Guid scopeGuidValue = new Guid(); bool haveScopeGuidValue = false; if (isInterface && interfaceGuid != null) { haveInterfaceGuidValue = Guid.TryParse(interfaceGuid, out interfaceGuidValue); if (haveInterfaceGuidValue) { // To have consistent errors. scope = null; identifier = null; } } if (scope != null) { haveScopeGuidValue = Guid.TryParse(scope, out scopeGuidValue); } foreach (AssemblySymbol assembly in referringAssembly.GetNoPiaResolutionAssemblies()) { Debug.Assert((object)assembly != null); if (ReferenceEquals(assembly, referringAssembly)) { continue; } // Ignore type forwarders NamedTypeSymbol? candidate = assembly.LookupDeclaredTopLevelMetadataType(ref name); Debug.Assert(candidate?.IsGenericType != true); Debug.Assert(candidate?.IsErrorType() != true); Debug.Assert(candidate is null || ReferenceEquals(candidate.ContainingAssembly, assembly)); // Ignore non-public types if (candidate is null || candidate.DeclaredAccessibility != Accessibility.Public) { continue; } // Ignore NoPia local types. // If candidate is coming from metadata, we don't need to do any special check, // because we do not create symbols for local types. However, local types defined in source // is another story. However, if compilation explicitly defines a local type, it should be // represented by a retargeting assembly, which is supposed to hide the local type. Debug.Assert(!(assembly is SourceAssemblySymbol) || !((SourceAssemblySymbol)assembly).SourceModule.MightContainNoPiaLocalTypes()); string candidateGuid; bool haveCandidateGuidValue = false; Guid candidateGuidValue = new Guid(); // The type must be of the same kind (interface, struct, delegate or enum). switch (candidate.TypeKind) { case TypeKind.Interface: if (!isInterface) { continue; } // Get candidate's Guid if (candidate.GetGuidString(out candidateGuid) && candidateGuid != null) { haveCandidateGuidValue = Guid.TryParse(candidateGuid, out candidateGuidValue); } break; case TypeKind.Delegate: case TypeKind.Enum: case TypeKind.Struct: if (isInterface) { continue; } // Let's use a trick. To make sure the kind is the same, make sure // base type is the same. SpecialType baseSpecialType = (candidate.BaseTypeNoUseSiteDiagnostics?.SpecialType ?? SpecialType.None); if (baseSpecialType == SpecialType.None || baseSpecialType != (baseType?.SpecialType ?? SpecialType.None)) { continue; } break; default: continue; } if (haveInterfaceGuidValue || haveCandidateGuidValue) { if (!haveInterfaceGuidValue || !haveCandidateGuidValue || candidateGuidValue != interfaceGuidValue) { continue; } } else { if (!haveScopeGuidValue || identifier == null || !identifier.Equals(name.FullName)) { continue; } // Scope guid must match candidate's assembly guid. haveCandidateGuidValue = false; if (assembly.GetGuidString(out candidateGuid) && candidateGuid != null) { haveCandidateGuidValue = Guid.TryParse(candidateGuid, out candidateGuidValue); } if (!haveCandidateGuidValue || scopeGuidValue != candidateGuidValue) { continue; } } // OK. It looks like we found canonical type definition. if ((object?)result != null) { // Ambiguity result = new NoPiaAmbiguousCanonicalTypeSymbol(referringAssembly, result, candidate); break; } result = candidate; } if ((object?)result == null) { result = new NoPiaMissingCanonicalTypeSymbol( referringAssembly, name.FullName, interfaceGuid, scope, identifier); } return result; } #nullable disable protected override MethodSymbol FindMethodSymbolInType(TypeSymbol typeSymbol, MethodDefinitionHandle targetMethodDef) { Debug.Assert(typeSymbol.IsDefinition); if (typeSymbol is PENamedTypeSymbol peTypeSymbol && ReferenceEquals(peTypeSymbol.ContainingPEModule, moduleSymbol)) { foreach (Symbol member in typeSymbol.GetMembersUnordered()) { PEMethodSymbol method = member as PEMethodSymbol; if ((object)method != null && method.Handle == targetMethodDef) { return method; } } } else if (typeSymbol is not ErrorTypeSymbol) { // We're going to use a special decoder that can generate usable symbols for type parameters without full context. // (We're not just using a different type - we're also changing the type context.) var memberRefDecoder = new MemberRefMetadataDecoder(moduleSymbol, typeSymbol); return (MethodSymbol)memberRefDecoder.FindMember(targetMethodDef, methodsOnly: true); } return null; } protected override FieldSymbol FindFieldSymbolInType(TypeSymbol typeSymbol, FieldDefinitionHandle fieldDef) { Debug.Assert(typeSymbol is PENamedTypeSymbol || typeSymbol is ErrorTypeSymbol); foreach (Symbol member in typeSymbol.GetMembersUnordered()) { PEFieldSymbol field = member as PEFieldSymbol; if ((object)field != null && field.Handle == fieldDef) { return field; } } return null; } internal override Symbol GetSymbolForMemberRef(MemberReferenceHandle memberRef, TypeSymbol scope = null, bool methodsOnly = false) { TypeSymbol targetTypeSymbol = GetMemberRefTypeSymbol(memberRef); if (targetTypeSymbol is null) { return null; } Debug.Assert(!targetTypeSymbol.IsTupleType); if ((object)scope != null) { Debug.Assert(scope.Kind == SymbolKind.NamedType || scope.Kind == SymbolKind.ErrorType); // We only want to consider members that are at or above "scope" in the type hierarchy. var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded; if (!TypeSymbol.Equals(scope, targetTypeSymbol, TypeCompareKind.ConsiderEverything2) && !(targetTypeSymbol.IsInterfaceType() ? scope.AllInterfacesNoUseSiteDiagnostics.IndexOf((NamedTypeSymbol)targetTypeSymbol, 0, SymbolEqualityComparer.CLRSignature) != -1 : scope.IsDerivedFrom(targetTypeSymbol, TypeCompareKind.CLRSignatureCompareOptions, useSiteInfo: ref discardedUseSiteInfo))) { return null; } } if (!targetTypeSymbol.IsTupleType) { targetTypeSymbol = TupleTypeDecoder.DecodeTupleTypesIfApplicable(targetTypeSymbol, elementNames: default); } // We're going to use a special decoder that can generate usable symbols for type parameters without full context. // (We're not just using a different type - we're also changing the type context.) var memberRefDecoder = new MemberRefMetadataDecoder(moduleSymbol, targetTypeSymbol.OriginalDefinition); var definition = memberRefDecoder.FindMember(memberRef, methodsOnly); if (definition is not null && !targetTypeSymbol.IsDefinition) { return definition.SymbolAsMember((NamedTypeSymbol)targetTypeSymbol); } return definition; } protected override void EnqueueTypeSymbolInterfacesAndBaseTypes(Queue<TypeDefinitionHandle> typeDefsToSearch, Queue<TypeSymbol> typeSymbolsToSearch, TypeSymbol typeSymbol) { foreach (NamedTypeSymbol @interface in typeSymbol.InterfacesNoUseSiteDiagnostics()) { EnqueueTypeSymbol(typeDefsToSearch, typeSymbolsToSearch, @interface); } EnqueueTypeSymbol(typeDefsToSearch, typeSymbolsToSearch, typeSymbol.BaseTypeNoUseSiteDiagnostics); } protected override void EnqueueTypeSymbol(Queue<TypeDefinitionHandle> typeDefsToSearch, Queue<TypeSymbol> typeSymbolsToSearch, TypeSymbol typeSymbol) { if ((object)typeSymbol != null) { PENamedTypeSymbol peTypeSymbol = typeSymbol as PENamedTypeSymbol; if ((object)peTypeSymbol != null && ReferenceEquals(peTypeSymbol.ContainingPEModule, moduleSymbol)) { typeDefsToSearch.Enqueue(peTypeSymbol.Handle); } else { typeSymbolsToSearch.Enqueue(typeSymbol); } } } protected override MethodDefinitionHandle GetMethodHandle(MethodSymbol method) { PEMethodSymbol peMethod = method as PEMethodSymbol; if ((object)peMethod != null && ReferenceEquals(peMethod.ContainingModule, moduleSymbol)) { return peMethod.Handle; } return default(MethodDefinitionHandle); } } }