|
// 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.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.Emit.NoPia;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
using ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer;
namespace Microsoft.CodeAnalysis.Emit
{
internal abstract class CommonPEModuleBuilder : Cci.IUnit, Cci.IModuleReference
{
internal readonly DebugDocumentsBuilder DebugDocumentsBuilder;
internal readonly IEnumerable<ResourceDescription> ManifestResources;
internal readonly Cci.ModulePropertiesForSerialization SerializationProperties;
internal readonly OutputKind OutputKind;
internal Stream? RawWin32Resources;
internal IEnumerable<Cci.IWin32Resource>? Win32Resources;
internal Cci.ResourceSection? Win32ResourceSection;
internal Stream? SourceLinkStreamOpt;
internal Cci.IMethodReference? PEEntryPoint;
internal Cci.IMethodReference? DebugEntryPoint;
private readonly ConcurrentDictionary<IMethodSymbolInternal, Cci.IMethodBody> _methodBodyMap;
private readonly TokenMap _referencesInILMap = new();
private readonly ItemTokenMap<string> _stringsInILMap = new();
private readonly ItemTokenMap<Cci.DebugSourceDocument> _sourceDocumentsInILMap = new();
private ImmutableArray<Cci.AssemblyReferenceAlias> _lazyAssemblyReferenceAliases;
private ImmutableArray<Cci.ManagedResource> _lazyManagedResources;
private IEnumerable<EmbeddedText> _embeddedTexts = SpecializedCollections.EmptyEnumerable<EmbeddedText>();
// Only set when running tests to allow inspection of the emitted data.
internal CompilationTestData? TestData { get; private set; }
internal EmitOptions EmitOptions { get; }
public CommonPEModuleBuilder(
IEnumerable<ResourceDescription> manifestResources,
EmitOptions emitOptions,
OutputKind outputKind,
Cci.ModulePropertiesForSerialization serializationProperties,
Compilation compilation)
{
Debug.Assert(manifestResources != null);
Debug.Assert(serializationProperties != null);
Debug.Assert(compilation != null);
ManifestResources = manifestResources;
DebugDocumentsBuilder = new DebugDocumentsBuilder(compilation.Options.SourceReferenceResolver, compilation.IsCaseSensitive);
OutputKind = outputKind;
SerializationProperties = serializationProperties;
_methodBodyMap = new ConcurrentDictionary<IMethodSymbolInternal, Cci.IMethodBody>(ReferenceEqualityComparer.Instance);
EmitOptions = emitOptions;
}
internal DebugInformationFormat DebugInformationFormat => EmitOptions.DebugInformationFormat;
internal HashAlgorithmName PdbChecksumAlgorithm => EmitOptions.PdbChecksumAlgorithm;
/// <summary>
/// Symbol changes when emitting EnC delta.
/// </summary>
public abstract SymbolChanges? EncSymbolChanges { get; }
/// <summary>
/// Previous EnC generation baseline, or null if this is not EnC delta.
/// </summary>
public abstract EmitBaseline? PreviousGeneration { get; }
/// <summary>
/// True if this module is an EnC update.
/// </summary>
public bool IsEncDelta => PreviousGeneration != null;
/// <summary>
/// EnC generation. 0 if the module is not an EnC delta, 1 if it is the first EnC delta, etc.
/// </summary>
public int CurrentGenerationOrdinal => (PreviousGeneration?.Ordinal + 1) ?? 0;
/// <summary>
/// Creates the type definition of HotReloadException type if it has not been synthesized yet and returns its constructor.
/// </summary>
public abstract IMethodSymbolInternal GetOrCreateHotReloadExceptionConstructorDefinition();
/// <summary>
/// Creates the type definition of HotReloadException type if it has not been synthesized yet and the module is an EnC delta.
/// Returns the synthesized type definition or null if the module is not an EnC delta or a user-defined type is already defined in the compilation.
/// </summary>
public abstract INamedTypeSymbolInternal? TryGetOrCreateSynthesizedHotReloadExceptionType();
/// <summary>
/// Returns the HotReloadException type symbol if it has been used in this compilation, null otherwise.
/// </summary>
public abstract INamedTypeSymbolInternal? GetUsedSynthesizedHotReloadExceptionType();
#nullable disable
/// <summary>
/// If this module represents an assembly, name of the assembly used in AssemblyDef table. Otherwise name of the module same as <see cref="ModuleName"/>.
/// </summary>
public abstract string Name { get; }
/// <summary>
/// Name of the module. Used in ModuleDef table.
/// </summary>
internal abstract string ModuleName { get; }
internal abstract Cci.IAssemblyReference Translate(IAssemblySymbolInternal symbol, DiagnosticBag diagnostics);
internal abstract Cci.ITypeReference Translate(ITypeSymbolInternal symbol, SyntaxNode syntaxOpt, DiagnosticBag diagnostics);
internal abstract Cci.IMethodReference Translate(IMethodSymbolInternal symbol, DiagnosticBag diagnostics, bool needDeclaration);
internal abstract Compilation CommonCompilation { get; }
internal abstract IModuleSymbolInternal CommonSourceModule { get; }
internal abstract IAssemblySymbolInternal CommonCorLibrary { get; }
internal abstract CommonModuleCompilationState CommonModuleCompilationState { get; }
internal abstract void CompilationFinished();
internal abstract ImmutableDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>> GetAllSynthesizedMembers();
internal abstract CommonEmbeddedTypesManager CommonEmbeddedTypesManagerOpt { get; }
internal abstract Cci.ITypeReference EncTranslateType(ITypeSymbolInternal type, DiagnosticBag diagnostics);
public abstract IEnumerable<Cci.ICustomAttribute> GetSourceAssemblyAttributes(bool isRefAssembly);
public abstract IEnumerable<Cci.SecurityAttribute> GetSourceAssemblySecurityAttributes();
public abstract IEnumerable<Cci.ICustomAttribute> GetSourceModuleAttributes();
internal abstract Cci.ICustomAttribute SynthesizeAttribute(WellKnownMember attributeConstructor);
/// <summary>
/// Public types defined in other modules making up this assembly and to which other assemblies may refer to via this assembly
/// followed by types forwarded to another assembly.
/// </summary>
public abstract ImmutableArray<Cci.ExportedType> GetExportedTypes(DiagnosticBag diagnostics);
/// <summary>
/// Used to distinguish which style to pick while writing native PDB information.
/// </summary>
/// <remarks>
/// The PDB content for custom debug information is different between Visual Basic and CSharp.
/// E.g. C# always includes a CustomMetadata Header (MD2) that contains the namespace scope counts, where
/// as VB only outputs namespace imports into the namespace scopes.
/// C# defines forwards in that header, VB includes them into the scopes list.
///
/// Currently the compiler doesn't allow mixing C# and VB method bodies. Thus this flag can be per module.
/// It is possible to move this flag to per-method basis but native PDB CDI forwarding would need to be adjusted accordingly.
/// </remarks>
public abstract bool GenerateVisualBasicStylePdb { get; }
/// <summary>
/// Linked assembly names to be stored to native PDB (VB only).
/// </summary>
public abstract IEnumerable<string> LinkedAssembliesDebugInfo { get; }
/// <summary>
/// Project level imports (VB only, TODO: C# scripts).
/// </summary>
public abstract ImmutableArray<Cci.UsedNamespaceOrType> GetImports();
/// <summary>
/// Default namespace (VB only).
/// </summary>
public abstract string DefaultNamespace { get; }
protected abstract Cci.IAssemblyReference GetCorLibraryReferenceToEmit(EmitContext context);
protected abstract IEnumerable<Cci.IAssemblyReference> GetAssemblyReferencesFromAddedModules(DiagnosticBag diagnostics);
protected abstract void AddEmbeddedResourcesFromAddedModules(ArrayBuilder<Cci.ManagedResource> builder, DiagnosticBag diagnostics);
public abstract Cci.ITypeReference GetPlatformType(Cci.PlatformType platformType, EmitContext context);
public abstract bool IsPlatformType(Cci.ITypeReference typeRef, Cci.PlatformType platformType);
#nullable enable
public abstract IEnumerable<Cci.INamespaceTypeDefinition> GetTopLevelTypeDefinitions(EmitContext context);
public IEnumerable<Cci.INamespaceTypeDefinition> GetTopLevelTypeDefinitionsCore(EmitContext context)
{
foreach (var typeDef in GetAnonymousTypeDefinitions(context))
{
yield return typeDef;
}
foreach (var typeDef in GetAdditionalTopLevelTypeDefinitions(context))
{
yield return typeDef;
}
foreach (var typeDef in GetEmbeddedTypeDefinitions(context))
{
yield return typeDef;
}
foreach (var typeDef in GetTopLevelSourceTypeDefinitions(context))
{
yield return typeDef;
}
var privateImpl = GetFrozenPrivateImplementationDetails();
if (privateImpl != null)
{
yield return privateImpl;
foreach (var typeDef in privateImpl.GetAdditionalTopLevelTypes())
{
yield return typeDef;
}
}
}
public abstract PrivateImplementationDetails? GetFrozenPrivateImplementationDetails();
/// <summary>
/// Additional top-level types injected by the Expression Evaluators.
/// </summary>
public abstract IEnumerable<Cci.INamespaceTypeDefinition> GetAdditionalTopLevelTypeDefinitions(EmitContext context);
/// <summary>
/// Anonymous types defined in the compilation.
/// </summary>
public abstract IEnumerable<Cci.INamespaceTypeDefinition> GetAnonymousTypeDefinitions(EmitContext context);
/// <summary>
/// Top-level embedded types (e.g. attribute types that are not present in referenced assemblies).
/// </summary>
public abstract IEnumerable<Cci.INamespaceTypeDefinition> GetEmbeddedTypeDefinitions(EmitContext context);
/// <summary>
/// Top-level named types defined in source.
/// </summary>
public abstract IEnumerable<Cci.INamespaceTypeDefinition> GetTopLevelSourceTypeDefinitions(EmitContext context);
/// <summary>
/// A list of the files that constitute the assembly. Empty for netmodule. These are not the source language files that may have been
/// used to compile the assembly, but the files that contain constituent modules of a multi-module assembly as well
/// as any external resources. It corresponds to the File table of the .NET assembly file format.
/// </summary>
public abstract IEnumerable<Cci.IFileReference> GetFiles(EmitContext context);
/// <summary>
/// Builds symbol definition to location map used for emitting token -> location info
/// into PDB to be consumed by WinMdExp.exe tool (only applicable for /t:winmdobj)
/// </summary>
public abstract MultiDictionary<Cci.DebugSourceDocument, Cci.DefinitionWithLocation> GetSymbolToLocationMap();
/// <summary>
/// Builds a list of types, and their documents, that would otherwise not be referenced by any document info
/// of any methods in those types, or any nested types. This data is helpful for navigating to the source of
/// types that have no methods in one or more of the source files they are contained in.
///
/// For example:
///
/// First.cs:
/// <code>
/// partial class Outer
/// {
/// partial class Inner
/// {
/// public void Method()
/// {
/// }
/// }
/// }
/// </code>
///
/// /// Second.cs:
/// <code>
/// partial class Outer
/// {
/// partial class Inner
/// {
/// }
/// }
/// </code>
///
/// When navigating to the definition of "Outer" we know about First.cs because of the MethodDebugInfo for Outer.Inner.Method()
/// but there would be no document information for Second.cs so this method would return that information.
///
/// When navigating to "Inner" we likewise know about First.cs because of the MethodDebugInfo, and we know about Second.cs because
/// of the document info for its containing type, so this method would not return information for Inner. In fact this method
/// will never return information for any nested type.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public abstract IEnumerable<(Cci.ITypeDefinition, ImmutableArray<Cci.DebugSourceDocument>)> GetTypeToDebugDocumentMap(EmitContext context);
#nullable disable
bool Cci.IDefinition.IsEncDeleted => false;
/// <summary>
/// Number of debug documents in the module.
/// Used to determine capacities of lists and indices when emitting debug info.
/// </summary>
public int DebugDocumentCount => DebugDocumentsBuilder.DebugDocumentCount;
public void Dispatch(Cci.MetadataVisitor visitor) => visitor.Visit(this);
IEnumerable<Cci.ICustomAttribute> Cci.IReference.GetAttributes(EmitContext context) => SpecializedCollections.EmptyEnumerable<Cci.ICustomAttribute>();
Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context)
{
Debug.Assert(ReferenceEquals(context.Module, this));
return this;
}
Symbols.ISymbolInternal Cci.IReference.GetInternalSymbol() => null;
public abstract ISourceAssemblySymbolInternal SourceAssemblyOpt { get; }
/// <summary>
/// An approximate number of method definitions that can
/// provide a basis for approximating the capacities of
/// various databases used during Emit.
/// </summary>
public int HintNumberOfMethodDefinitions
// Try to guess at the size of tables to prevent re-allocation. The method body
// map is pretty close, but unfortunately it tends to undercount. x1.5 seems like
// a healthy amount of room based on compiling Roslyn.
=> (int)(_methodBodyMap.Count * 1.5);
#nullable enable
internal Cci.IMethodBody? GetMethodBody(IMethodSymbolInternal methodSymbol)
{
Debug.Assert(methodSymbol.ContainingModule == CommonSourceModule);
Debug.Assert(methodSymbol.IsDefinition);
Debug.Assert(((IMethodSymbol)methodSymbol.GetISymbol()).PartialDefinitionPart == null); // Must be definition.
Cci.IMethodBody? body;
if (_methodBodyMap.TryGetValue(methodSymbol, out body))
{
return body;
}
return null;
}
#nullable disable
public void SetMethodBody(IMethodSymbolInternal methodSymbol, Cci.IMethodBody body)
{
Debug.Assert(methodSymbol.ContainingModule == CommonSourceModule);
Debug.Assert(methodSymbol.IsDefinition);
Debug.Assert(((IMethodSymbol)methodSymbol.GetISymbol()).PartialDefinitionPart == null); // Must be definition.
Debug.Assert(body == null || (object)methodSymbol == body.MethodDefinition.GetInternalSymbol());
_methodBodyMap.Add(methodSymbol, body);
}
internal void SetPEEntryPoint(IMethodSymbolInternal method, DiagnosticBag diagnostics)
{
Debug.Assert(method == null || IsSourceDefinition(method));
Debug.Assert(OutputKind.IsApplication());
PEEntryPoint = Translate(method, diagnostics, needDeclaration: true);
}
internal void SetDebugEntryPoint(IMethodSymbolInternal method, DiagnosticBag diagnostics)
{
Debug.Assert(method == null || IsSourceDefinition(method));
DebugEntryPoint = Translate(method, diagnostics, needDeclaration: true);
}
private bool IsSourceDefinition(IMethodSymbolInternal method)
{
return method.ContainingModule == CommonSourceModule && method.IsDefinition;
}
/// <summary>
/// CorLibrary assembly referenced by this module.
/// </summary>
public Cci.IAssemblyReference GetCorLibrary(EmitContext context)
{
return Translate(CommonCorLibrary, context.Diagnostics);
}
public Cci.IAssemblyReference GetContainingAssembly(EmitContext context)
{
return OutputKind == OutputKind.NetModule ? null : (Cci.IAssemblyReference)this;
}
/// <summary>
/// Returns copy of User Strings referenced from the IL in the module.
/// </summary>
public string[] CopyStrings()
{
return _stringsInILMap.CopyItems();
}
public uint GetFakeSymbolTokenForIL(Cci.IReference symbol, SyntaxNode syntaxNode, DiagnosticBag diagnostics)
{
uint token = _referencesInILMap.GetOrAddTokenFor(symbol, out bool added);
if (added)
{
ReferenceDependencyWalker.VisitReference(symbol, new EmitContext(this, syntaxNode, diagnostics, metadataOnly: false, includePrivateMembers: true));
}
return token;
}
public uint GetFakeSymbolTokenForIL(Cci.ISignature symbol, SyntaxNode syntaxNode, DiagnosticBag diagnostics)
{
uint token = _referencesInILMap.GetOrAddTokenFor(symbol, out bool added);
if (added)
{
ReferenceDependencyWalker.VisitSignature(symbol, new EmitContext(this, syntaxNode, diagnostics, metadataOnly: false, includePrivateMembers: true));
}
return token;
}
public uint GetSourceDocumentIndexForIL(Cci.DebugSourceDocument document)
{
return _sourceDocumentsInILMap.GetOrAddTokenFor(document);
}
internal Cci.DebugSourceDocument GetSourceDocumentFromIndex(uint token)
{
return _sourceDocumentsInILMap.GetItem(token);
}
public object GetReferenceFromToken(uint token)
{
return _referencesInILMap.GetItem(token);
}
public uint GetFakeStringTokenForIL(string str)
{
return _stringsInILMap.GetOrAddTokenFor(str);
}
public string GetStringFromToken(uint token)
{
return _stringsInILMap.GetItem(token);
}
public ReadOnlySpan<object> ReferencesInIL()
{
return _referencesInILMap.GetAllItems();
}
/// <summary>
/// Assembly reference aliases (C# only).
/// </summary>
public ImmutableArray<Cci.AssemblyReferenceAlias> GetAssemblyReferenceAliases(EmitContext context)
{
if (_lazyAssemblyReferenceAliases.IsDefault)
{
ImmutableInterlocked.InterlockedCompareExchange(ref _lazyAssemblyReferenceAliases, CalculateAssemblyReferenceAliases(context), default(ImmutableArray<Cci.AssemblyReferenceAlias>));
}
return _lazyAssemblyReferenceAliases;
}
private ImmutableArray<Cci.AssemblyReferenceAlias> CalculateAssemblyReferenceAliases(EmitContext context)
{
var result = ArrayBuilder<Cci.AssemblyReferenceAlias>.GetInstance();
foreach (var assemblyAndAliases in CommonCompilation.GetBoundReferenceManager().GetReferencedAssemblyAliases())
{
var assembly = assemblyAndAliases.Item1;
var aliases = assemblyAndAliases.Item2;
for (int i = 0; i < aliases.Length; i++)
{
string alias = aliases[i];
// filter out duplicates and global aliases:
if (alias != MetadataReferenceProperties.GlobalAlias && aliases.IndexOf(alias, 0, i) < 0)
{
result.Add(new Cci.AssemblyReferenceAlias(alias, Translate(assembly, context.Diagnostics)));
}
}
}
return result.ToImmutableAndFree();
}
public IEnumerable<Cci.IAssemblyReference> GetAssemblyReferences(EmitContext context)
{
Cci.IAssemblyReference corLibrary = GetCorLibraryReferenceToEmit(context);
// Only add Cor Library reference explicitly, PeWriter will add
// other references implicitly on as needed basis.
if (corLibrary != null)
{
yield return corLibrary;
}
if (OutputKind != OutputKind.NetModule)
{
// Explicitly add references from added modules
foreach (var aRef in GetAssemblyReferencesFromAddedModules(context.Diagnostics))
{
yield return aRef;
}
}
}
public ImmutableArray<Cci.ManagedResource> GetResources(EmitContext context)
{
if (context.IsRefAssembly)
{
// Manifest resources are not included in ref assemblies
// Ref assemblies don't support added modules
return ImmutableArray<Cci.ManagedResource>.Empty;
}
if (_lazyManagedResources.IsDefault)
{
var builder = ArrayBuilder<Cci.ManagedResource>.GetInstance();
foreach (ResourceDescription r in ManifestResources)
{
builder.Add(r.ToManagedResource());
}
if (OutputKind != OutputKind.NetModule)
{
// Explicitly add resources from added modules
AddEmbeddedResourcesFromAddedModules(builder, context.Diagnostics);
}
_lazyManagedResources = builder.ToImmutableAndFree();
}
return _lazyManagedResources;
}
public IEnumerable<EmbeddedText> EmbeddedTexts
{
get
{
return _embeddedTexts;
}
set
{
Debug.Assert(value != null);
_embeddedTexts = value;
}
}
internal void SetTestData(CompilationTestData testData)
{
Debug.Assert(TestData == null);
TestData = testData;
testData.Module = this;
}
public int GetTypeDefinitionGeneration(Cci.INamedTypeDefinition typeDef)
{
if (PreviousGeneration != null)
{
var symbolChanges = EncSymbolChanges!;
if (symbolChanges.IsReplacedDef(typeDef))
{
// Type emitted with Replace semantics in this delta, it's name should have the current generation ordinal suffix.
return CurrentGenerationOrdinal;
}
var previousTypeDef = symbolChanges.DefinitionMap.MapDefinition(typeDef);
if (previousTypeDef != null && PreviousGeneration.GenerationOrdinals.TryGetValue(previousTypeDef, out int lastEmittedOrdinal))
{
// Type previously emitted with Replace semantics is now updated in-place. Use the ordinal used to emit the last version of the type.
return lastEmittedOrdinal;
}
}
return 0;
}
}
/// <summary>
/// Common base class for C# and VB PE module builder.
/// </summary>
internal abstract class PEModuleBuilder<TCompilation, TSourceModuleSymbol, TAssemblySymbol, TTypeSymbol, TNamedTypeSymbol, TMethodSymbol, TSyntaxNode, TEmbeddedTypesManager, TModuleCompilationState> : CommonPEModuleBuilder, ITokenDeferral
where TCompilation : Compilation
where TSourceModuleSymbol : class, IModuleSymbolInternal
where TAssemblySymbol : class, IAssemblySymbolInternal
where TTypeSymbol : class, ITypeSymbolInternal
where TNamedTypeSymbol : class, TTypeSymbol, INamedTypeSymbolInternal
where TMethodSymbol : class, IMethodSymbolInternal
where TSyntaxNode : SyntaxNode
where TEmbeddedTypesManager : CommonEmbeddedTypesManager
where TModuleCompilationState : ModuleCompilationState<TNamedTypeSymbol, TMethodSymbol>
{
internal readonly TSourceModuleSymbol SourceModule;
internal readonly TCompilation Compilation;
private PrivateImplementationDetails _lazyPrivateImplementationDetails;
private ArrayMethods _lazyArrayMethods;
private HashSet<string> _namesOfTopLevelTypes;
internal readonly TModuleCompilationState CompilationState;
private readonly Cci.RootModuleType _rootModuleType;
public abstract TEmbeddedTypesManager EmbeddedTypesManagerOpt { get; }
protected PEModuleBuilder(
TCompilation compilation,
TSourceModuleSymbol sourceModule,
Cci.ModulePropertiesForSerialization serializationProperties,
IEnumerable<ResourceDescription> manifestResources,
OutputKind outputKind,
EmitOptions emitOptions,
TModuleCompilationState compilationState)
: base(manifestResources, emitOptions, outputKind, serializationProperties, compilation)
{
Debug.Assert(sourceModule != null);
Debug.Assert(serializationProperties != null);
Compilation = compilation;
SourceModule = sourceModule;
this.CompilationState = compilationState;
_rootModuleType = new Cci.RootModuleType(this);
}
public Cci.RootModuleType RootModuleType => _rootModuleType;
internal sealed override void CompilationFinished()
{
this.CompilationState.Freeze();
}
internal override IAssemblySymbolInternal CommonCorLibrary => CorLibrary;
internal abstract TAssemblySymbol CorLibrary { get; }
internal abstract Cci.INamedTypeReference GetSpecialType(SpecialType specialType, TSyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics);
internal sealed override Cci.ITypeReference EncTranslateType(ITypeSymbolInternal type, DiagnosticBag diagnostics)
{
return EncTranslateLocalVariableType((TTypeSymbol)type, diagnostics);
}
internal virtual Cci.ITypeReference EncTranslateLocalVariableType(TTypeSymbol type, DiagnosticBag diagnostics)
{
return Translate(type, null, diagnostics);
}
protected bool HaveDeterminedTopLevelTypes
{
get { return _namesOfTopLevelTypes != null; }
}
protected bool ContainsTopLevelType(string fullEmittedName)
{
return _namesOfTopLevelTypes.Contains(fullEmittedName);
}
/// <summary>
/// Returns all top-level (not nested) types defined in the module.
/// </summary>
public override IEnumerable<Cci.INamespaceTypeDefinition> GetTopLevelTypeDefinitions(EmitContext context)
{
Cci.TypeReferenceIndexer typeReferenceIndexer = null;
HashSet<string> names;
// First time through, we need to collect emitted names of all top level types.
if (_namesOfTopLevelTypes == null)
{
names = new HashSet<string>();
}
else
{
names = null;
}
// First time through, we need to push things through TypeReferenceIndexer
// to make sure we collect all to be embedded NoPia types and members.
if (EmbeddedTypesManagerOpt != null && !EmbeddedTypesManagerOpt.IsFrozen)
{
typeReferenceIndexer = new Cci.TypeReferenceIndexer(context);
Debug.Assert(names != null);
// Run this reference indexer on the assembly- and module-level attributes first.
// We'll run it on all other types below.
// The purpose is to trigger Translate on all types.
Dispatch(typeReferenceIndexer);
}
AddTopLevelType(names, RootModuleType);
VisitTopLevelType(typeReferenceIndexer, RootModuleType);
yield return RootModuleType;
foreach (var typeDef in GetTopLevelTypeDefinitionsCore(context))
{
AddTopLevelType(names, typeDef);
VisitTopLevelType(typeReferenceIndexer, typeDef);
yield return typeDef;
}
if (EmbeddedTypesManagerOpt != null)
{
foreach (var embedded in EmbeddedTypesManagerOpt.GetTypes(context.Diagnostics, names))
{
AddTopLevelType(names, embedded);
yield return embedded;
}
}
if (names != null)
{
Debug.Assert(_namesOfTopLevelTypes == null);
_namesOfTopLevelTypes = names;
}
static void AddTopLevelType(HashSet<string> names, Cci.INamespaceTypeDefinition type)
// _namesOfTopLevelTypes are only used to generated exported types, which are not emitted in EnC deltas (hence generation 0):
=> names?.Add(MetadataHelpers.BuildQualifiedName(type.NamespaceName, Cci.MetadataWriter.GetMetadataName(type, generation: 0)));
}
public virtual ImmutableArray<TNamedTypeSymbol> GetAdditionalTopLevelTypes()
=> ImmutableArray<TNamedTypeSymbol>.Empty;
public virtual ImmutableArray<TNamedTypeSymbol> GetEmbeddedTypes(DiagnosticBag diagnostics)
=> ImmutableArray<TNamedTypeSymbol>.Empty;
internal abstract Cci.IAssemblyReference Translate(TAssemblySymbol symbol, DiagnosticBag diagnostics);
internal abstract Cci.ITypeReference Translate(TTypeSymbol symbol, TSyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics);
internal abstract Cci.IMethodReference Translate(TMethodSymbol symbol, DiagnosticBag diagnostics, bool needDeclaration);
internal sealed override Cci.IAssemblyReference Translate(IAssemblySymbolInternal symbol, DiagnosticBag diagnostics)
{
return Translate((TAssemblySymbol)symbol, diagnostics);
}
internal sealed override Cci.ITypeReference Translate(ITypeSymbolInternal symbol, SyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics)
{
return Translate((TTypeSymbol)symbol, (TSyntaxNode)syntaxNodeOpt, diagnostics);
}
internal sealed override Cci.IMethodReference Translate(IMethodSymbolInternal symbol, DiagnosticBag diagnostics, bool needDeclaration)
{
return Translate((TMethodSymbol)symbol, diagnostics, needDeclaration);
}
internal sealed override IModuleSymbolInternal CommonSourceModule => SourceModule;
internal sealed override Compilation CommonCompilation => Compilation;
internal sealed override CommonModuleCompilationState CommonModuleCompilationState => CompilationState;
internal sealed override CommonEmbeddedTypesManager CommonEmbeddedTypesManagerOpt => EmbeddedTypesManagerOpt;
internal MetadataConstant CreateConstant(
TTypeSymbol type,
object value,
TSyntaxNode syntaxNodeOpt,
DiagnosticBag diagnostics)
{
return new MetadataConstant(Translate(type, syntaxNodeOpt, diagnostics), value);
}
private static void VisitTopLevelType(Cci.TypeReferenceIndexer noPiaIndexer, Cci.INamespaceTypeDefinition type)
{
noPiaIndexer?.Visit((Cci.ITypeDefinition)type);
}
internal Cci.IFieldReference GetModuleVersionId(Cci.ITypeReference mvidType, TSyntaxNode syntaxOpt, DiagnosticBag diagnostics)
{
PrivateImplementationDetails details = GetPrivateImplClass(syntaxOpt, diagnostics);
EnsurePrivateImplementationDetailsStaticConstructor(details, syntaxOpt, diagnostics);
return details.GetModuleVersionId(mvidType);
}
internal Cci.IFieldReference GetModuleCancellationToken(Cci.ITypeReference cancellationTokenType, TSyntaxNode syntaxOpt, DiagnosticBag diagnostics)
=> GetPrivateImplClass(syntaxOpt, diagnostics).GetModuleCancellationToken(cancellationTokenType);
internal Cci.IFieldReference GetInstrumentationPayloadRoot(int analysisKind, Cci.ITypeReference payloadType, TSyntaxNode syntaxOpt, DiagnosticBag diagnostics)
{
PrivateImplementationDetails details = GetPrivateImplClass(syntaxOpt, diagnostics);
EnsurePrivateImplementationDetailsStaticConstructor(details, syntaxOpt, diagnostics);
return details.GetOrAddInstrumentationPayloadRoot(analysisKind, payloadType);
}
private void EnsurePrivateImplementationDetailsStaticConstructor(PrivateImplementationDetails details, TSyntaxNode syntaxOpt, DiagnosticBag diagnostics)
{
if (details.GetMethod(WellKnownMemberNames.StaticConstructorName) == null)
{
Cci.IMethodDefinition cctor = CreatePrivateImplementationDetailsStaticConstructor(syntaxOpt, diagnostics);
Debug.Assert(((ISynthesizedGlobalMethodSymbol)cctor.GetInternalSymbol()).ContainingPrivateImplementationDetailsType == (object)details);
details.TryAddSynthesizedMethod(cctor);
}
}
protected abstract Cci.IMethodDefinition CreatePrivateImplementationDetailsStaticConstructor(TSyntaxNode syntaxOpt, DiagnosticBag diagnostics);
#region Synthesized Members
/// <summary>
/// Captures the set of synthesized definitions that should be added to a type
/// during emit process.
/// </summary>
private sealed class SynthesizedDefinitions
{
private ConcurrentQueue<Cci.INestedTypeDefinition> NestedTypes;
public ConcurrentQueue<Cci.IMethodDefinition> Methods;
public ConcurrentQueue<Cci.IPropertyDefinition> Properties;
public ConcurrentQueue<Cci.IFieldDefinition> Fields;
// Nested types may be queued from concurrent threads, but we need to emit them
// in a deterministic order.
internal IEnumerable<Cci.INestedTypeDefinition> OrderedNestedTypes
{
get
{
// We don't synthesize nested types with different arities for a given name
Debug.Assert(NestedTypes is null ||
NestedTypes.Select(t => t.Name).Distinct().Count() == NestedTypes.Count());
return NestedTypes?.OrderBy(t => t.Name, StringComparer.Ordinal);
}
}
internal void AddNestedType(Cci.INestedTypeDefinition nestedType)
{
if (NestedTypes == null)
{
Interlocked.CompareExchange(ref NestedTypes, new ConcurrentQueue<Cci.INestedTypeDefinition>(), null);
}
NestedTypes.Enqueue(nestedType);
}
public ImmutableArray<ISymbolInternal> GetAllMembers()
{
var builder = ArrayBuilder<ISymbolInternal>.GetInstance();
if (Fields != null)
{
foreach (var field in Fields)
{
builder.Add(field.GetInternalSymbol());
}
}
if (Methods != null)
{
foreach (var method in Methods)
{
builder.Add(method.GetInternalSymbol());
}
}
if (Properties != null)
{
foreach (var property in Properties)
{
builder.Add(property.GetInternalSymbol());
}
}
if (NestedTypes != null)
{
foreach (var type in OrderedNestedTypes)
{
builder.Add(type.GetInternalSymbol());
}
}
return builder.ToImmutableAndFree();
}
}
private readonly ConcurrentDictionary<TNamedTypeSymbol, SynthesizedDefinitions> _synthesizedTypeMembers =
new ConcurrentDictionary<TNamedTypeSymbol, SynthesizedDefinitions>(ReferenceEqualityComparer.Instance);
private ConcurrentDictionary<INamespaceSymbolInternal, ConcurrentQueue<INamespaceOrTypeSymbolInternal>> _lazySynthesizedNamespaceMembers;
internal abstract IEnumerable<Cci.INestedTypeDefinition> GetSynthesizedNestedTypes(TNamedTypeSymbol container);
/// <summary>
/// Returns null if there are no compiler generated types.
/// </summary>
public IEnumerable<Cci.INestedTypeDefinition> GetSynthesizedTypes(TNamedTypeSymbol container)
{
IEnumerable<Cci.INestedTypeDefinition> declareTypes = GetSynthesizedNestedTypes(container);
IEnumerable<Cci.INestedTypeDefinition> compileEmitTypes = null;
if (_synthesizedTypeMembers.TryGetValue(container, out var defs))
{
compileEmitTypes = defs.OrderedNestedTypes;
}
if (declareTypes == null)
{
return compileEmitTypes;
}
if (compileEmitTypes == null)
{
return declareTypes;
}
return declareTypes.Concat(compileEmitTypes);
}
private SynthesizedDefinitions GetOrAddSynthesizedDefinitions(TNamedTypeSymbol container)
{
Debug.Assert(container.IsDefinition);
return _synthesizedTypeMembers.GetOrAdd(container, _ => new SynthesizedDefinitions());
}
public void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.IMethodDefinition method)
{
Debug.Assert(method != null);
SynthesizedDefinitions defs = GetOrAddSynthesizedDefinitions(container);
if (defs.Methods == null)
{
Interlocked.CompareExchange(ref defs.Methods, new ConcurrentQueue<Cci.IMethodDefinition>(), null);
}
defs.Methods.Enqueue(method);
}
public void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.IPropertyDefinition property)
{
Debug.Assert(property != null);
SynthesizedDefinitions defs = GetOrAddSynthesizedDefinitions(container);
if (defs.Properties == null)
{
Interlocked.CompareExchange(ref defs.Properties, new ConcurrentQueue<Cci.IPropertyDefinition>(), null);
}
defs.Properties.Enqueue(property);
}
public void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.IFieldDefinition field)
{
Debug.Assert(field != null);
SynthesizedDefinitions defs = GetOrAddSynthesizedDefinitions(container);
if (defs.Fields == null)
{
Interlocked.CompareExchange(ref defs.Fields, new ConcurrentQueue<Cci.IFieldDefinition>(), null);
}
defs.Fields.Enqueue(field);
}
public void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.INestedTypeDefinition nestedType)
{
Debug.Assert(nestedType != null);
SynthesizedDefinitions defs = GetOrAddSynthesizedDefinitions(container);
defs.AddNestedType(nestedType);
}
public void AddSynthesizedDefinition(INamespaceSymbolInternal container, INamespaceOrTypeSymbolInternal typeOrNamespace)
{
Debug.Assert(typeOrNamespace != null);
if (_lazySynthesizedNamespaceMembers == null)
{
Interlocked.CompareExchange(ref _lazySynthesizedNamespaceMembers, new ConcurrentDictionary<INamespaceSymbolInternal, ConcurrentQueue<INamespaceOrTypeSymbolInternal>>(), null);
}
_lazySynthesizedNamespaceMembers.GetOrAdd(container, _ => new ConcurrentQueue<INamespaceOrTypeSymbolInternal>()).Enqueue(typeOrNamespace);
}
/// <summary>
/// Returns null if there are no synthesized fields.
/// </summary>
public IEnumerable<Cci.IFieldDefinition> GetSynthesizedFields(TNamedTypeSymbol container)
=> _synthesizedTypeMembers.TryGetValue(container, out var defs) ? defs.Fields : null;
/// <summary>
/// Returns null if there are no synthesized properties.
/// </summary>
public IEnumerable<Cci.IPropertyDefinition> GetSynthesizedProperties(TNamedTypeSymbol container)
=> _synthesizedTypeMembers.TryGetValue(container, out var defs) ? defs.Properties : null;
/// <summary>
/// Returns null if there are no synthesized methods.
/// </summary>
public IEnumerable<Cci.IMethodDefinition> GetSynthesizedMethods(TNamedTypeSymbol container)
=> _synthesizedTypeMembers.TryGetValue(container, out var defs) ? defs.Methods : null;
internal override ImmutableDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>> GetAllSynthesizedMembers()
{
var builder = ImmutableDictionary.CreateBuilder<ISymbolInternal, ImmutableArray<ISymbolInternal>>();
foreach (var entry in _synthesizedTypeMembers)
{
builder.Add(entry.Key, entry.Value.GetAllMembers());
}
var namespaceMembers = _lazySynthesizedNamespaceMembers;
if (namespaceMembers != null)
{
foreach (var entry in namespaceMembers)
{
builder.Add(entry.Key, entry.Value.ToImmutableArray<ISymbolInternal>());
}
}
// Add synthesized HotReloadException if it has been used in emitted code.
// We do not add it at the creation time since we don't know if it's going to be used at that point.
if (GetUsedSynthesizedHotReloadExceptionType() is { } hotReloadException)
{
if (!builder.TryGetValue(hotReloadException.ContainingNamespace, out var existingTypes))
{
existingTypes = [];
}
builder[hotReloadException.ContainingNamespace] = existingTypes.Add(hotReloadException);
}
return builder.ToImmutable();
}
#endregion
#region Token Mapping
Cci.IFieldReference ITokenDeferral.GetFieldForData(ImmutableArray<byte> data, ushort alignment, SyntaxNode syntaxNode, DiagnosticBag diagnostics)
{
RoslynDebug.Assert(alignment is 1 or 2 or 4 or 8, $"Unexpected alignment: {alignment}");
var privateImpl = GetPrivateImplClass((TSyntaxNode)syntaxNode, diagnostics);
// map a field to the block (that makes it addressable via a token)
return privateImpl.CreateDataField(data, alignment);
}
Cci.IFieldReference ITokenDeferral.GetArrayCachingFieldForData(ImmutableArray<byte> data, Cci.IArrayTypeReference arrayType, SyntaxNode syntaxNode, DiagnosticBag diagnostics)
{
var privateImpl = GetPrivateImplClass((TSyntaxNode)syntaxNode, diagnostics);
var emitContext = new EmitContext(this, syntaxNode, diagnostics, metadataOnly: false, includePrivateMembers: true);
// map a field to the block (that makes it addressable via a token)
return privateImpl.CreateArrayCachingField(data, arrayType, emitContext);
}
public Cci.IFieldReference GetArrayCachingFieldForConstants(ImmutableArray<ConstantValue> constants, Cci.IArrayTypeReference arrayType, SyntaxNode syntaxNode, DiagnosticBag diagnostics)
{
var privateImpl = GetPrivateImplClass((TSyntaxNode)syntaxNode, diagnostics);
var emitContext = new EmitContext(this, syntaxNode, diagnostics, metadataOnly: false, includePrivateMembers: true);
return privateImpl.CreateArrayCachingField(constants, arrayType, emitContext);
}
public abstract Cci.IMethodReference GetInitArrayHelper();
public ArrayMethods ArrayMethods
{
get
{
ArrayMethods result = _lazyArrayMethods;
if (result == null)
{
result = new ArrayMethods();
if (Interlocked.CompareExchange(ref _lazyArrayMethods, result, null) != null)
{
result = _lazyArrayMethods;
}
}
return result;
}
}
#endregion
#region Private Implementation Details Type
#nullable enable
internal PrivateImplementationDetails GetPrivateImplClass(TSyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics)
{
var result = _lazyPrivateImplementationDetails;
if (result == null)
{
result = new PrivateImplementationDetails(
this,
this.SourceModule.Name,
Compilation.GetSubmissionSlotIndex(),
this.GetSpecialType(SpecialType.System_Object, syntaxNodeOpt, diagnostics),
this.GetSpecialType(SpecialType.System_ValueType, syntaxNodeOpt, diagnostics),
this.GetSpecialType(SpecialType.System_Byte, syntaxNodeOpt, diagnostics),
this.GetSpecialType(SpecialType.System_Int16, syntaxNodeOpt, diagnostics),
this.GetSpecialType(SpecialType.System_Int32, syntaxNodeOpt, diagnostics),
this.GetSpecialType(SpecialType.System_Int64, syntaxNodeOpt, diagnostics),
SynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor));
if (Interlocked.CompareExchange(ref _lazyPrivateImplementationDetails, result, null) != null)
{
result = _lazyPrivateImplementationDetails;
}
}
return result;
}
public PrivateImplementationDetails? FreezePrivateImplementationDetails()
{
_lazyPrivateImplementationDetails?.Freeze();
return _lazyPrivateImplementationDetails;
}
public override PrivateImplementationDetails? GetFrozenPrivateImplementationDetails()
{
Debug.Assert(_lazyPrivateImplementationDetails?.IsFrozen != false);
return _lazyPrivateImplementationDetails;
}
#nullable disable
#endregion
public sealed override Cci.ITypeReference GetPlatformType(Cci.PlatformType platformType, EmitContext context)
{
Debug.Assert((object)this == context.Module);
switch (platformType)
{
case Cci.PlatformType.SystemType:
throw ExceptionUtilities.UnexpectedValue(platformType);
default:
return GetSpecialType((SpecialType)platformType, (TSyntaxNode)context.SyntaxNode, context.Diagnostics);
}
}
}
}
|