| File: Symbols\NonMissingAssemblySymbol.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// 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.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; using System.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp.Symbols { /// <summary> /// A <see cref="NonMissingAssemblySymbol"/> is a special kind of <see cref="AssemblySymbol"/> that represents /// an assembly that is not missing, i.e. the "real" thing. /// </summary> internal abstract class NonMissingAssemblySymbol : AssemblySymbol { /// <summary> /// This is a cache similar to the one used by MetaImport::GetTypeByName /// in native compiler. The difference is that native compiler pre-populates /// the cache when it loads types. Here we are populating the cache only /// with things we looked for, so that next time we are looking for the same /// thing, the lookup is fast. This cache also takes care of TypeForwarders. /// Gives about 8% win on subsequent lookups in some scenarios. /// </summary> /// <remarks></remarks> private readonly ConcurrentDictionary<MetadataTypeName.Key, NamedTypeSymbol> _emittedNameToTypeMap = new ConcurrentDictionary<MetadataTypeName.Key, NamedTypeSymbol>(); private NamespaceSymbol? _globalNamespace; /// <summary> /// Does this symbol represent a missing assembly. /// </summary> internal sealed override bool IsMissing { get { return false; } } /// <summary> /// Gets the merged root namespace that contains all namespaces and types defined in the modules /// of this assembly. If there is just one module in this assembly, this property just returns the /// GlobalNamespace of that module. /// </summary> public sealed override NamespaceSymbol GlobalNamespace { get { if ((object?)_globalNamespace == null) { // Get the root namespace from each module, and merge them all together. If there is only one, // then MergedNamespaceSymbol.Create will just return that one. IEnumerable<NamespaceSymbol> allGlobalNamespaces = from m in Modules select m.GlobalNamespace; var result = MergedNamespaceSymbol.Create(new NamespaceExtent(this), null, allGlobalNamespaces.AsImmutable()); Interlocked.CompareExchange(ref _globalNamespace, result, null); } return _globalNamespace; } } /// <summary> /// Lookup a top level type referenced from metadata, names should be /// compared case-sensitively. Detect cycles during lookup. /// </summary> /// <param name="emittedName"> /// Full type name, possibly with generic name mangling. /// </param> internal sealed override NamedTypeSymbol? LookupDeclaredTopLevelMetadataType(ref MetadataTypeName emittedName) { NamedTypeSymbol? result = null; // This is a cache similar to the one used by MetaImport::GetTypeByName in native // compiler. The difference is that native compiler pre-populates the cache when it // loads types. Here we are populating the cache only with things we looked for, so that // next time we are looking for the same thing, the lookup is fast. This cache also // takes care of TypeForwarders. Gives about 8% win on subsequent lookups in some // scenarios. // // CONSIDER !!! // // However, it is questionable how often subsequent lookup by name is going to happen. // Currently it doesn't happen for TypeDef tokens at all, for TypeRef tokens, the // lookup by name is done once and the result is cached. So, multiple lookups by name // for the same type are going to happen only in these cases: // 1) Resolving GetType() in attribute application, type is encoded by name. // 2) TypeRef token isn't reused within the same module, i.e. multiple TypeRefs point to // the same type. // 3) Different Module refers to the same type, lookup once per Module (with exception of #2). // 4) Multitargeting - retargeting the type to a different version of assembly result = LookupTopLevelMetadataTypeInCache(ref emittedName); if ((object?)result != null) { // We cache result equivalent to digging through type forwarders, which // might produce a forwarder specific ErrorTypeSymbol. We don't want to // return that error symbol. if (!result.IsErrorType() && (object)result.ContainingAssembly == (object)this) { return result; } // According to the cache, the type wasn't found, or isn't declared in this assembly (forwarded). return null; } else { result = LookupDeclaredTopLevelMetadataTypeInModules(ref emittedName); Debug.Assert(result is null || ((object)result.ContainingAssembly == (object)this && !result.IsErrorType())); if (result is null) { return null; } // Add result of the lookup into the cache return CacheTopLevelMetadataType(ref emittedName, result); } } private NamedTypeSymbol? LookupDeclaredTopLevelMetadataTypeInModules(ref MetadataTypeName emittedName) { // Now we will look for the type in each module of the assembly and pick the first type // we find, this is what native VB compiler does. foreach (var module in this.Modules) { NamedTypeSymbol? result = module.LookupTopLevelMetadataType(ref emittedName); if (result is not null) { return result; } } return null; } /// <summary> /// Lookup a top level type referenced from metadata, names should be /// compared case-sensitively. Detect cycles during lookup. /// </summary> /// <param name="emittedName"> /// Full type name, possibly with generic name mangling. /// </param> /// <param name="visitedAssemblies"> /// List of assemblies lookup has already visited (since type forwarding can introduce cycles). /// </param> internal sealed override NamedTypeSymbol LookupDeclaredOrForwardedTopLevelMetadataType(ref MetadataTypeName emittedName, ConsList<AssemblySymbol>? visitedAssemblies) { NamedTypeSymbol? result = LookupTopLevelMetadataTypeInCache(ref emittedName); if ((object?)result != null) { return result; } else { result = LookupDeclaredTopLevelMetadataTypeInModules(ref emittedName); Debug.Assert(result is null || ((object)result.ContainingAssembly == (object)this && !result.IsErrorType())); if (result is null) { // We didn't find the type result = TryLookupForwardedMetadataTypeWithCycleDetection(ref emittedName, visitedAssemblies); } // Add result of the lookup into the cache return CacheTopLevelMetadataType(ref emittedName, result ?? new MissingMetadataTypeSymbol.TopLevel(this.Modules[0], ref emittedName)); } } internal abstract override NamedTypeSymbol? TryLookupForwardedMetadataTypeWithCycleDetection(ref MetadataTypeName emittedName, ConsList<AssemblySymbol>? visitedAssemblies); private NamedTypeSymbol? LookupTopLevelMetadataTypeInCache(ref MetadataTypeName emittedName) { NamedTypeSymbol? result; if (_emittedNameToTypeMap.TryGetValue(emittedName.ToKey(), out result)) { return result; } return null; } /// <summary> /// For test purposes only. /// </summary> internal NamedTypeSymbol CachedTypeByEmittedName(string emittedname) { MetadataTypeName mdName = MetadataTypeName.FromFullName(emittedname); return _emittedNameToTypeMap[mdName.ToKey()]; } /// <summary> /// For test purposes only. /// </summary> internal int EmittedNameToTypeMapCount { get { return _emittedNameToTypeMap.Count; } } private NamedTypeSymbol CacheTopLevelMetadataType( ref MetadataTypeName emittedName, NamedTypeSymbol result) { NamedTypeSymbol result1; result1 = _emittedNameToTypeMap.GetOrAdd(emittedName.ToKey(), result); System.Diagnostics.Debug.Assert(TypeSymbol.Equals(result1, result, TypeCompareKind.ConsiderEverything2)); // object identity may differ in error cases return result1; } } }