// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using System.Text; using System.Threading; using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Symbols; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { /// /// The compilation object is an immutable representation of a single invocation of the /// compiler. Although immutable, a compilation is also on-demand, and will realize and cache /// data as necessary. A compilation can produce a new compilation from existing compilation /// with the application of small deltas. In many cases, it is more efficient than creating a /// new compilation from scratch, as the new compilation can reuse information from the old /// compilation. /// public abstract partial class Compilation { /// /// Returns true if this is a case sensitive compilation, false otherwise. Case sensitivity /// affects compilation features such as name lookup as well as choosing what names to emit /// when there are multiple different choices (for example between a virtual method and an /// override). /// public abstract bool IsCaseSensitive { get; } /// /// Used for test purposes only to emulate missing members. /// private SmallDictionary _lazyMakeWellKnownTypeMissingMap; /// /// Used for test purposes only to emulate missing members. /// private SmallDictionary _lazyMakeMemberMissingMap; private readonly IReadOnlyDictionary _features; public ScriptCompilationInfo ScriptCompilationInfo => CommonScriptCompilationInfo; internal abstract ScriptCompilationInfo CommonScriptCompilationInfo { get; } internal Compilation( string name, ImmutableArray references, IReadOnlyDictionary features, bool isSubmission, AsyncQueue eventQueue) { Debug.Assert(!references.IsDefault); Debug.Assert(features != null); this.AssemblyName = name; this.ExternalReferences = references; this.EventQueue = eventQueue; _lazySubmissionSlotIndex = isSubmission ? SubmissionSlotIndexToBeAllocated : SubmissionSlotIndexNotApplicable; _features = features; } protected static IReadOnlyDictionary SyntaxTreeCommonFeatures(IEnumerable trees) { IReadOnlyDictionary set = null; foreach (var tree in trees) { var treeFeatures = tree.Options.Features; if (set == null) { set = treeFeatures; } else { if ((object)set != treeFeatures && !set.SetEquals(treeFeatures)) { throw new ArgumentException(CodeAnalysisResources.InconsistentSyntaxTreeFeature, nameof(trees)); } } } if (set == null) { // Edge case where there are no syntax trees set = ImmutableDictionary.Empty; } return set; } internal abstract AnalyzerDriver AnalyzerForLanguage(ImmutableArray analyzers, AnalyzerManager analyzerManager); /// /// Gets the source language ("C#" or "Visual Basic"). /// public abstract string Language { get; } internal static void ValidateScriptCompilationParameters(Compilation previousScriptCompilation, Type returnType, ref Type globalsType) { if (globalsType != null && !IsValidHostObjectType(globalsType)) { throw new ArgumentException(CodeAnalysisResources.ReturnTypeCannotBeValuePointerbyRefOrOpen, nameof(globalsType)); } if (returnType != null && !IsValidSubmissionReturnType(returnType)) { throw new ArgumentException(CodeAnalysisResources.ReturnTypeCannotBeVoidByRefOrOpen, nameof(returnType)); } if (previousScriptCompilation != null) { if (globalsType == null) { globalsType = previousScriptCompilation.HostObjectType; } else if (globalsType != previousScriptCompilation.HostObjectType) { throw new ArgumentException(CodeAnalysisResources.TypeMustBeSameAsHostObjectTypeOfPreviousSubmission, nameof(globalsType)); } // Force the previous submission to be analyzed. This is required for anonymous types unification. if (previousScriptCompilation.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error)) { throw new InvalidOperationException(CodeAnalysisResources.PreviousSubmissionHasErrors); } } } /// /// Checks options passed to submission compilation constructor. /// Throws an exception if the options are not applicable to submissions. /// internal static void CheckSubmissionOptions(CompilationOptions options) { if (options == null) { return; } if (options.OutputKind.IsValid() && options.OutputKind != OutputKind.DynamicallyLinkedLibrary) { throw new ArgumentException(CodeAnalysisResources.InvalidOutputKindForSubmission, nameof(options)); } if (options.CryptoKeyContainer != null || options.CryptoKeyFile != null || options.DelaySign != null || !options.CryptoPublicKey.IsEmpty || (options.DelaySign == true && options.PublicSign)) { throw new ArgumentException(CodeAnalysisResources.InvalidCompilationOptions, nameof(options)); } } /// /// Creates a new compilation equivalent to this one with different symbol instances. /// public Compilation Clone() { return CommonClone(); } protected abstract Compilation CommonClone(); /// /// Returns a new compilation with a given event queue. /// internal abstract Compilation WithEventQueue(AsyncQueue eventQueue); /// /// Gets a new for the specified syntax tree. /// /// The specified syntax tree. /// /// True if the SemanticModel should ignore accessibility rules when answering semantic questions. /// public SemanticModel GetSemanticModel(SyntaxTree syntaxTree, bool ignoreAccessibility = false) { return CommonGetSemanticModel(syntaxTree, ignoreAccessibility); } protected abstract SemanticModel CommonGetSemanticModel(SyntaxTree syntaxTree, bool ignoreAccessibility); /// /// Returns a new INamedTypeSymbol representing an error type with the given name and arity /// in the given optional container. /// public INamedTypeSymbol CreateErrorTypeSymbol(INamespaceOrTypeSymbol container, string name, int arity) { if (name == null) { throw new ArgumentNullException(nameof(name)); } if (arity < 0) { throw new ArgumentException($"{nameof(arity)} must be >= 0", nameof(arity)); } return CommonCreateErrorTypeSymbol(container, name, arity); } protected abstract INamedTypeSymbol CommonCreateErrorTypeSymbol(INamespaceOrTypeSymbol container, string name, int arity); /// /// Returns a new INamespaceSymbol representing an error (missing) namespace with the given name. /// public INamespaceSymbol CreateErrorNamespaceSymbol(INamespaceSymbol container, string name) { if (container == null) { throw new ArgumentNullException(nameof(container)); } if (name == null) { throw new ArgumentNullException(nameof(name)); } return CommonCreateErrorNamespaceSymbol(container, name); } protected abstract INamespaceSymbol CommonCreateErrorNamespaceSymbol(INamespaceSymbol container, string name); #region Name internal const string UnspecifiedModuleAssemblyName = "?"; /// /// Simple assembly name, or null if not specified. /// /// /// The name is used for determining internals-visible-to relationship with referenced assemblies. /// /// If the compilation represents an assembly the value of is its simple name. /// /// Unless specifies otherwise the module name /// written to metadata is with an extension based upon . /// public string AssemblyName { get; } internal void CheckAssemblyName(DiagnosticBag diagnostics) { // We could only allow name == null if OutputKind is Module. // However, it does no harm that we allow name == null for assemblies as well, so we don't enforce it. if (this.AssemblyName != null) { MetadataHelpers.CheckAssemblyOrModuleName(this.AssemblyName, MessageProvider, MessageProvider.ERR_BadAssemblyName, diagnostics); } } internal string MakeSourceAssemblySimpleName() { return AssemblyName ?? UnspecifiedModuleAssemblyName; } internal string MakeSourceModuleName() { return Options.ModuleName ?? (AssemblyName != null ? AssemblyName + Options.OutputKind.GetDefaultExtension() : UnspecifiedModuleAssemblyName); } /// /// Creates a compilation with the specified assembly name. /// /// The new assembly name. /// A new compilation. public Compilation WithAssemblyName(string assemblyName) { return CommonWithAssemblyName(assemblyName); } protected abstract Compilation CommonWithAssemblyName(string outputName); #endregion #region Options /// /// Gets the options the compilation was created with. /// public CompilationOptions Options { get { return CommonOptions; } } protected abstract CompilationOptions CommonOptions { get; } /// /// Creates a new compilation with the specified compilation options. /// /// The new options. /// A new compilation. public Compilation WithOptions(CompilationOptions options) { return CommonWithOptions(options); } protected abstract Compilation CommonWithOptions(CompilationOptions options); #endregion #region Submissions // An index in the submission slot array. Allocated lazily in compilation phase based upon the slot index of the previous submission. // Special values: // -1 ... neither this nor previous submissions in the chain allocated a slot (the submissions don't contain code) // -2 ... the slot of this submission hasn't been determined yet // -3 ... this is not a submission compilation private int _lazySubmissionSlotIndex; private const int SubmissionSlotIndexNotApplicable = -3; private const int SubmissionSlotIndexToBeAllocated = -2; /// /// True if the compilation represents an interactive submission. /// internal bool IsSubmission { get { return _lazySubmissionSlotIndex != SubmissionSlotIndexNotApplicable; } } /// /// Gets or allocates a runtime submission slot index for this compilation. /// /// Non-negative integer if this is a submission and it or a previous submission contains code, negative integer otherwise. internal int GetSubmissionSlotIndex() { if (_lazySubmissionSlotIndex == SubmissionSlotIndexToBeAllocated) { // TODO (tomat): remove recursion int lastSlotIndex = ScriptCompilationInfo.PreviousScriptCompilation?.GetSubmissionSlotIndex() ?? 0; _lazySubmissionSlotIndex = HasCodeToEmit() ? lastSlotIndex + 1 : lastSlotIndex; } return _lazySubmissionSlotIndex; } // The type of interactive submission result requested by the host, or null if this compilation doesn't represent a submission. // // The type is resolved to a symbol when the Script's instance ctor symbol is constructed. The symbol needs to be resolved against // the references of this compilation. // // Consider (tomat): As an alternative to Reflection Type we could hold onto any piece of information that lets us // resolve the type symbol when needed. /// /// The type object that represents the type of submission result the host requested. /// internal Type SubmissionReturnType => ScriptCompilationInfo?.ReturnTypeOpt; internal static bool IsValidSubmissionReturnType(Type type) { return !(type == typeof(void) || type.IsByRef || type.GetTypeInfo().ContainsGenericParameters); } /// /// The type of the globals object or null if not specified for this compilation. /// internal Type HostObjectType => ScriptCompilationInfo?.GlobalsType; internal static bool IsValidHostObjectType(Type type) { var info = type.GetTypeInfo(); return !(info.IsValueType || info.IsPointer || info.IsByRef || info.ContainsGenericParameters); } internal abstract bool HasSubmissionResult(); public Compilation WithScriptCompilationInfo(ScriptCompilationInfo info) => CommonWithScriptCompilationInfo(info); protected abstract Compilation CommonWithScriptCompilationInfo(ScriptCompilationInfo info); #endregion #region Syntax Trees /// /// Gets the syntax trees (parsed from source code) that this compilation was created with. /// public IEnumerable SyntaxTrees { get { return CommonSyntaxTrees; } } protected abstract IEnumerable CommonSyntaxTrees { get; } /// /// Creates a new compilation with additional syntax trees. /// /// The new syntax trees. /// A new compilation. public Compilation AddSyntaxTrees(params SyntaxTree[] trees) { return CommonAddSyntaxTrees(trees); } /// /// Creates a new compilation with additional syntax trees. /// /// The new syntax trees. /// A new compilation. public Compilation AddSyntaxTrees(IEnumerable trees) { return CommonAddSyntaxTrees(trees); } protected abstract Compilation CommonAddSyntaxTrees(IEnumerable trees); /// /// Creates a new compilation without the specified syntax trees. Preserves metadata info for use with trees /// added later. /// /// The new syntax trees. /// A new compilation. public Compilation RemoveSyntaxTrees(params SyntaxTree[] trees) { return CommonRemoveSyntaxTrees(trees); } /// /// Creates a new compilation without the specified syntax trees. Preserves metadata info for use with trees /// added later. /// /// The new syntax trees. /// A new compilation. public Compilation RemoveSyntaxTrees(IEnumerable trees) { return CommonRemoveSyntaxTrees(trees); } protected abstract Compilation CommonRemoveSyntaxTrees(IEnumerable trees); /// /// Creates a new compilation without any syntax trees. Preserves metadata info for use with /// trees added later. /// public Compilation RemoveAllSyntaxTrees() { return CommonRemoveAllSyntaxTrees(); } protected abstract Compilation CommonRemoveAllSyntaxTrees(); /// /// Creates a new compilation with an old syntax tree replaced with a new syntax tree. /// Reuses metadata from old compilation object. /// /// The new tree. /// The old tree. /// A new compilation. public Compilation ReplaceSyntaxTree(SyntaxTree oldTree, SyntaxTree newTree) { return CommonReplaceSyntaxTree(oldTree, newTree); } protected abstract Compilation CommonReplaceSyntaxTree(SyntaxTree oldTree, SyntaxTree newTree); /// /// Returns true if this compilation contains the specified tree. False otherwise. /// /// A syntax tree. public bool ContainsSyntaxTree(SyntaxTree syntaxTree) { return CommonContainsSyntaxTree(syntaxTree); } protected abstract bool CommonContainsSyntaxTree(SyntaxTree syntaxTree); /// /// The event queue that this compilation was created with. /// internal readonly AsyncQueue EventQueue; #endregion #region References internal static ImmutableArray ValidateReferences(IEnumerable references) where T : CompilationReference { var result = references.AsImmutableOrEmpty(); for (int i = 0; i < result.Length; i++) { var reference = result[i]; if (reference == null) { throw new ArgumentNullException($"{nameof(references)}[{i}]"); } var peReference = reference as PortableExecutableReference; if (peReference == null && !(reference is T)) { Debug.Assert(reference is UnresolvedMetadataReference || reference is CompilationReference); throw new ArgumentException(string.Format(CodeAnalysisResources.ReferenceOfTypeIsInvalid1, reference.GetType()), $"{nameof(references)}[{i}]"); } } return result; } internal CommonReferenceManager GetBoundReferenceManager() { return CommonGetBoundReferenceManager(); } internal abstract CommonReferenceManager CommonGetBoundReferenceManager(); /// /// Metadata references passed to the compilation constructor. /// public ImmutableArray ExternalReferences { get; } /// /// Unique metadata references specified via #r directive in the source code of this compilation. /// public abstract ImmutableArray DirectiveReferences { get; } /// /// All reference directives used in this compilation. /// internal abstract IEnumerable ReferenceDirectives { get; } /// /// Maps values of #r references to resolved metadata references. /// internal abstract IDictionary<(string path, string content), MetadataReference> ReferenceDirectiveMap { get; } /// /// All metadata references -- references passed to the compilation /// constructor as well as references specified via #r directives. /// public IEnumerable References { get { foreach (var reference in ExternalReferences) { yield return reference; } foreach (var reference in DirectiveReferences) { yield return reference; } } } /// /// Creates a metadata reference for this compilation. /// /// /// Optional aliases that can be used to refer to the compilation root namespace via extern alias directive. /// /// /// Embed the COM types from the reference so that the compiled /// application no longer requires a primary interop assembly (PIA). /// public abstract CompilationReference ToMetadataReference(ImmutableArray aliases = default(ImmutableArray), bool embedInteropTypes = false); /// /// Creates a new compilation with the specified references. /// /// /// The new references. /// /// A new compilation. public Compilation WithReferences(IEnumerable newReferences) { return this.CommonWithReferences(newReferences); } /// /// Creates a new compilation with the specified references. /// /// The new references. /// A new compilation. public Compilation WithReferences(params MetadataReference[] newReferences) { return this.WithReferences((IEnumerable)newReferences); } /// /// Creates a new compilation with the specified references. /// protected abstract Compilation CommonWithReferences(IEnumerable newReferences); /// /// Creates a new compilation with additional metadata references. /// /// The new references. /// A new compilation. public Compilation AddReferences(params MetadataReference[] references) { return AddReferences((IEnumerable)references); } /// /// Creates a new compilation with additional metadata references. /// /// The new references. /// A new compilation. public Compilation AddReferences(IEnumerable references) { if (references == null) { throw new ArgumentNullException(nameof(references)); } if (references.IsEmpty()) { return this; } return CommonWithReferences(this.ExternalReferences.Union(references)); } /// /// Creates a new compilation without the specified metadata references. /// /// The new references. /// A new compilation. public Compilation RemoveReferences(params MetadataReference[] references) { return RemoveReferences((IEnumerable)references); } /// /// Creates a new compilation without the specified metadata references. /// /// The new references. /// A new compilation. public Compilation RemoveReferences(IEnumerable references) { if (references == null) { throw new ArgumentNullException(nameof(references)); } if (references.IsEmpty()) { return this; } var refSet = new HashSet(this.ExternalReferences); //EDMAURER if AddingReferences accepts duplicates, then a consumer supplying a list with //duplicates to add will not know exactly which to remove. Let them supply a list with //duplicates here. foreach (var r in references.Distinct()) { if (!refSet.Remove(r)) { throw new ArgumentException(string.Format(CodeAnalysisResources.MetadataRefNotFoundToRemove1, r), nameof(references)); } } return CommonWithReferences(refSet); } /// /// Creates a new compilation without any metadata references. /// public Compilation RemoveAllReferences() { return CommonWithReferences(SpecializedCollections.EmptyEnumerable()); } /// /// Creates a new compilation with an old metadata reference replaced with a new metadata /// reference. /// /// The new reference. /// The old reference. /// A new compilation. public Compilation ReplaceReference(MetadataReference oldReference, MetadataReference newReference) { if (oldReference == null) { throw new ArgumentNullException(nameof(oldReference)); } if (newReference == null) { return this.RemoveReferences(oldReference); } return this.RemoveReferences(oldReference).AddReferences(newReference); } /// /// Gets the or for a metadata reference used to create this /// compilation. /// /// The target reference. /// /// Assembly or module symbol corresponding to the given reference or null if there is none. /// public ISymbol GetAssemblyOrModuleSymbol(MetadataReference reference) { return CommonGetAssemblyOrModuleSymbol(reference); } protected abstract ISymbol CommonGetAssemblyOrModuleSymbol(MetadataReference reference); /// /// Gets the that corresponds to the assembly symbol. /// /// The target symbol. public MetadataReference GetMetadataReference(IAssemblySymbol assemblySymbol) { return GetBoundReferenceManager().GetMetadataReference(assemblySymbol); } /// /// Assembly identities of all assemblies directly referenced by this compilation. /// /// /// Includes identities of references passed in the compilation constructor /// as well as those specified via directives in source code. /// public abstract IEnumerable ReferencedAssemblyNames { get; } #endregion #region Symbols /// /// The that represents the assembly being created. /// public IAssemblySymbol Assembly { get { return CommonAssembly; } } protected abstract IAssemblySymbol CommonAssembly { get; } /// /// Gets the for the module being created by compiling all of /// the source code. /// public IModuleSymbol SourceModule { get { return CommonSourceModule; } } protected abstract IModuleSymbol CommonSourceModule { get; } /// /// The root namespace that contains all namespaces and types defined in source code or in /// referenced metadata, merged into a single namespace hierarchy. /// public INamespaceSymbol GlobalNamespace { get { return CommonGlobalNamespace; } } protected abstract INamespaceSymbol CommonGlobalNamespace { get; } /// /// Gets the corresponding compilation namespace for the specified module or assembly namespace. /// public INamespaceSymbol GetCompilationNamespace(INamespaceSymbol namespaceSymbol) { return CommonGetCompilationNamespace(namespaceSymbol); } protected abstract INamespaceSymbol CommonGetCompilationNamespace(INamespaceSymbol namespaceSymbol); internal abstract CommonAnonymousTypeManager CommonAnonymousTypeManager { get; } /// /// Returns the Main method that will serves as the entry point of the assembly, if it is /// executable (and not a script). /// public IMethodSymbol GetEntryPoint(CancellationToken cancellationToken) { return CommonGetEntryPoint(cancellationToken); } protected abstract IMethodSymbol CommonGetEntryPoint(CancellationToken cancellationToken); /// /// Get the symbol for the predefined type from the Cor Library referenced by this /// compilation. /// public INamedTypeSymbol GetSpecialType(SpecialType specialType) { return CommonGetSpecialType(specialType); } /// /// Returns true if the type is System.Type. /// internal abstract bool IsSystemTypeReference(ITypeSymbol type); protected abstract INamedTypeSymbol CommonGetSpecialType(SpecialType specialType); internal abstract ISymbol CommonGetWellKnownTypeMember(WellKnownMember member); /// /// Returns true if the specified type is equal to or derives from System.Attribute well-known type. /// internal abstract bool IsAttributeType(ITypeSymbol type); /// /// The INamedTypeSymbol for the .NET System.Object type, which could have a TypeKind of /// Error if there was no COR Library in this Compilation. /// public INamedTypeSymbol ObjectType { get { return CommonObjectType; } } protected abstract INamedTypeSymbol CommonObjectType { get; } /// /// The TypeSymbol for the type 'dynamic' in this Compilation. /// public ITypeSymbol DynamicType { get { return CommonDynamicType; } } protected abstract ITypeSymbol CommonDynamicType { get; } /// /// A symbol representing the implicit Script class. This is null if the class is not /// defined in the compilation. /// public INamedTypeSymbol ScriptClass { get { return CommonScriptClass; } } protected abstract INamedTypeSymbol CommonScriptClass { get; } /// /// Resolves a symbol that represents script container (Script class). Uses the /// full name of the container class stored in to find the symbol. /// /// The Script class symbol or null if it is not defined. protected INamedTypeSymbol CommonBindScriptClass() { string scriptClassName = this.Options.ScriptClassName ?? ""; string[] parts = scriptClassName.Split('.'); INamespaceSymbol container = this.SourceModule.GlobalNamespace; for (int i = 0; i < parts.Length - 1; i++) { INamespaceSymbol next = container.GetNestedNamespace(parts[i]); if (next == null) { AssertNoScriptTrees(); return null; } container = next; } foreach (INamedTypeSymbol candidate in container.GetTypeMembers(parts[parts.Length - 1])) { if (candidate.IsScriptClass) { return candidate; } } AssertNoScriptTrees(); return null; } [Conditional("DEBUG")] private void AssertNoScriptTrees() { foreach (var tree in this.SyntaxTrees) { Debug.Assert(tree.Options.Kind != SourceCodeKind.Script); } } /// /// Returns a new ArrayTypeSymbol representing an array type tied to the base types of the /// COR Library in this Compilation. /// public IArrayTypeSymbol CreateArrayTypeSymbol(ITypeSymbol elementType, int rank = 1) { return CommonCreateArrayTypeSymbol(elementType, rank); } protected abstract IArrayTypeSymbol CommonCreateArrayTypeSymbol(ITypeSymbol elementType, int rank); /// /// Returns a new PointerTypeSymbol representing a pointer type tied to a type in this /// Compilation. /// public IPointerTypeSymbol CreatePointerTypeSymbol(ITypeSymbol pointedAtType) { return CommonCreatePointerTypeSymbol(pointedAtType); } protected abstract IPointerTypeSymbol CommonCreatePointerTypeSymbol(ITypeSymbol elementType); /// /// Gets the type within the compilation's assembly and all referenced assemblies (other than /// those that can only be referenced via an extern alias) using its canonical CLR metadata name. /// /// Null if the type can't be found. /// /// Since VB does not have the concept of extern aliases, it considers all referenced assemblies. /// public INamedTypeSymbol GetTypeByMetadataName(string fullyQualifiedMetadataName) { return CommonGetTypeByMetadataName(fullyQualifiedMetadataName); } protected abstract INamedTypeSymbol CommonGetTypeByMetadataName(string metadataName); #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters /// /// Returns a new INamedTypeSymbol with the given element types and (optional) element names. /// public INamedTypeSymbol CreateTupleTypeSymbol( ImmutableArray elementTypes, ImmutableArray elementNames = default(ImmutableArray), ImmutableArray elementLocations = default(ImmutableArray)) { if (elementTypes.IsDefault) { throw new ArgumentNullException(nameof(elementTypes)); } if (elementTypes.Length <= 1) { throw new ArgumentException(CodeAnalysisResources.TuplesNeedAtLeastTwoElements, nameof(elementNames)); } elementNames = CheckTupleElementNames(elementTypes.Length, elementNames); CheckTupleElementLocations(elementTypes.Length, elementLocations); for (int i = 0, n = elementTypes.Length; i < n; i++) { if (elementTypes[i] == null) { throw new ArgumentNullException($"{nameof(elementTypes)}[{i}]"); } if (!elementLocations.IsDefault && elementLocations[i] == null) { throw new ArgumentNullException($"{nameof(elementLocations)}[{i}]"); } } return CommonCreateTupleTypeSymbol(elementTypes, elementNames, elementLocations); } #pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters /// /// Check that if any names are provided, and their number matches the expected cardinality. /// Returns a normalized version of the element names (empty array if all the names are null). /// protected static ImmutableArray CheckTupleElementNames(int cardinality, ImmutableArray elementNames) { if (!elementNames.IsDefault) { if (elementNames.Length != cardinality) { throw new ArgumentException(CodeAnalysisResources.TupleElementNameCountMismatch, nameof(elementNames)); } for (int i = 0; i < elementNames.Length; i++) { if (elementNames[i] == "") { throw new ArgumentException(CodeAnalysisResources.TupleElementNameEmpty, $"{nameof(elementNames)}[{i}]"); } } if (elementNames.All(n => n == null)) { return default(ImmutableArray); } } return elementNames; } protected static void CheckTupleElementLocations( int cardinality, ImmutableArray elementLocations) { if (!elementLocations.IsDefault) { if (elementLocations.Length != cardinality) { throw new ArgumentException(CodeAnalysisResources.TupleElementLocationCountMismatch, nameof(elementLocations)); } } } protected abstract INamedTypeSymbol CommonCreateTupleTypeSymbol( ImmutableArray elementTypes, ImmutableArray elementNames, ImmutableArray elementLocations); #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters /// /// Returns a new INamedTypeSymbol with the given underlying type and (optional) element names. /// /// /// Since VB doesn't support tuples yet, this call will fail in a VB compilation. /// Also, the underlying type needs to be tuple-compatible. /// public INamedTypeSymbol CreateTupleTypeSymbol( INamedTypeSymbol underlyingType, ImmutableArray elementNames = default(ImmutableArray), ImmutableArray elementLocations = default(ImmutableArray)) { if ((object)underlyingType == null) { throw new ArgumentNullException(nameof(underlyingType)); } return CommonCreateTupleTypeSymbol( underlyingType, elementNames, elementLocations); } #pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters protected abstract INamedTypeSymbol CommonCreateTupleTypeSymbol( INamedTypeSymbol underlyingType, ImmutableArray elementNames, ImmutableArray elementLocations); /// /// Returns a new anonymous type symbol with the given member types member names. /// Anonymous type members will be readonly by default. Writable properties are /// supported in VB and can be created by passing in false in the /// appropriate locations in . /// /// Source locations can also be provided through /// public INamedTypeSymbol CreateAnonymousTypeSymbol( ImmutableArray memberTypes, ImmutableArray memberNames, ImmutableArray memberIsReadOnly = default(ImmutableArray), ImmutableArray memberLocations = default(ImmutableArray)) { if (memberTypes.IsDefault) { throw new ArgumentNullException(nameof(memberTypes)); } if (memberNames.IsDefault) { throw new ArgumentNullException(nameof(memberNames)); } if (memberTypes.Length != memberNames.Length) { throw new ArgumentException(string.Format(CodeAnalysisResources.AnonymousTypeMemberAndNamesCountMismatch2, nameof(memberTypes), nameof(memberNames))); } if (!memberLocations.IsDefault && memberLocations.Length != memberTypes.Length) { throw new ArgumentException(string.Format(CodeAnalysisResources.AnonymousTypeArgumentCountMismatch2, nameof(memberLocations), nameof(memberNames))); } if (!memberIsReadOnly.IsDefault && memberIsReadOnly.Length != memberTypes.Length) { throw new ArgumentException(string.Format(CodeAnalysisResources.AnonymousTypeArgumentCountMismatch2, nameof(memberIsReadOnly), nameof(memberNames))); } for (int i = 0, n = memberTypes.Length; i < n; i++) { if (memberTypes[i] == null) { throw new ArgumentNullException($"{nameof(memberTypes)}[{i}]"); } if (memberNames[i] == null) { throw new ArgumentNullException($"{nameof(memberNames)}[{i}]"); } if (!memberLocations.IsDefault && memberLocations[i] == null) { throw new ArgumentNullException($"{nameof(memberLocations)}[{i}]"); } } return CommonCreateAnonymousTypeSymbol(memberTypes, memberNames, memberLocations, memberIsReadOnly); } protected abstract INamedTypeSymbol CommonCreateAnonymousTypeSymbol( ImmutableArray memberTypes, ImmutableArray memberNames, ImmutableArray memberLocations, ImmutableArray memberIsReadOnly); #endregion #region Diagnostics internal const CompilationStage DefaultDiagnosticsStage = CompilationStage.Compile; /// /// Gets the diagnostics produced during the parsing stage. /// public abstract ImmutableArray GetParseDiagnostics(CancellationToken cancellationToken = default(CancellationToken)); /// /// Gets the diagnostics produced during symbol declaration. /// public abstract ImmutableArray GetDeclarationDiagnostics(CancellationToken cancellationToken = default(CancellationToken)); /// /// Gets the diagnostics produced during the analysis of method bodies and field initializers. /// public abstract ImmutableArray GetMethodBodyDiagnostics(CancellationToken cancellationToken = default(CancellationToken)); /// /// Gets all the diagnostics for the compilation, including syntax, declaration, and /// binding. Does not include any diagnostics that might be produced during emit, see /// . /// public abstract ImmutableArray GetDiagnostics(CancellationToken cancellationToken = default(CancellationToken)); internal void EnsureCompilationEventQueueCompleted() { Debug.Assert(EventQueue != null); lock (EventQueue) { if (!EventQueue.IsCompleted) { CompleteCompilationEventQueue_NoLock(); } } } internal void CompleteCompilationEventQueue_NoLock() { Debug.Assert(EventQueue != null); // Signal the end of compilation. EventQueue.TryEnqueue(new CompilationCompletedEvent(this)); EventQueue.PromiseNotToEnqueue(); EventQueue.TryComplete(); } internal abstract CommonMessageProvider MessageProvider { get; } /// /// Filter out warnings based on the compiler options (/nowarn, /warn and /warnaserror) and the pragma warning directives. /// 'incoming' is freed. /// /// Bag to which filtered diagnostics will be added. /// Diagnostics to be filtered. /// True if there were no errors or warnings-as-errors. internal bool FilterAndAppendAndFreeDiagnostics(DiagnosticBag accumulator, ref DiagnosticBag incoming) { bool result = FilterAndAppendDiagnostics(accumulator, incoming.AsEnumerableWithoutResolution(), exclude: null); incoming.Free(); incoming = null; return result; } /// /// Filter out warnings based on the compiler options (/nowarn, /warn and /warnaserror) and the pragma warning directives. /// /// True when there is no error. internal bool FilterAndAppendDiagnostics(DiagnosticBag accumulator, IEnumerable incoming, HashSet exclude) { bool hasError = false; bool reportSuppressedDiagnostics = Options.ReportSuppressedDiagnostics; foreach (Diagnostic d in incoming) { if (exclude?.Contains(d.Code) == true) { continue; } var filtered = Options.FilterDiagnostic(d); if (filtered == null || (!reportSuppressedDiagnostics && filtered.IsSuppressed)) { continue; } else if (filtered.Severity == DiagnosticSeverity.Error) { hasError = true; } accumulator.Add(filtered); } return !hasError; } #endregion #region Resources /// /// Create a stream filled with default win32 resources. /// public Stream CreateDefaultWin32Resources(bool versionResource, bool noManifest, Stream manifestContents, Stream iconInIcoFormat) { //Win32 resource encodings use a lot of 16bit values. Do all of the math checked with the //expectation that integer types are well-chosen with size in mind. checked { var result = new MemoryStream(1024); //start with a null resource just as rc.exe does AppendNullResource(result); if (versionResource) AppendDefaultVersionResource(result); if (!noManifest) { if (this.Options.OutputKind.IsApplication()) { // Applications use a default manifest if one is not specified. if (manifestContents == null) { manifestContents = typeof(Compilation).GetTypeInfo().Assembly.GetManifestResourceStream("Microsoft.CodeAnalysis.Resources.default.win32manifest"); } } else { // Modules never have manifests, even if one is specified. //Debug.Assert(!this.Options.OutputKind.IsNetModule() || manifestContents == null); } if (manifestContents != null) { Win32ResourceConversions.AppendManifestToResourceStream(result, manifestContents, !this.Options.OutputKind.IsApplication()); } } if (iconInIcoFormat != null) { Win32ResourceConversions.AppendIconToResourceStream(result, iconInIcoFormat); } result.Position = 0; return result; } } internal static void AppendNullResource(Stream resourceStream) { var writer = new BinaryWriter(resourceStream); writer.Write((UInt32)0); writer.Write((UInt32)0x20); writer.Write((UInt16)0xFFFF); writer.Write((UInt16)0); writer.Write((UInt16)0xFFFF); writer.Write((UInt16)0); writer.Write((UInt32)0); //DataVersion writer.Write((UInt16)0); //MemoryFlags writer.Write((UInt16)0); //LanguageId writer.Write((UInt32)0); //Version writer.Write((UInt32)0); //Characteristics } protected abstract void AppendDefaultVersionResource(Stream resourceStream); internal enum Win32ResourceForm : byte { UNKNOWN, COFF, RES } internal static Win32ResourceForm DetectWin32ResourceForm(Stream win32Resources) { var reader = new BinaryReader(win32Resources, Encoding.Unicode); var initialPosition = win32Resources.Position; var initial32Bits = reader.ReadUInt32(); win32Resources.Position = initialPosition; //RC.EXE output starts with a resource that contains no data. if (initial32Bits == 0) return Win32ResourceForm.RES; else if ((initial32Bits & 0xFFFF0000) != 0 || (initial32Bits & 0x0000FFFF) != 0xFFFF) // See CLiteWeightStgdbRW::FindObjMetaData in peparse.cpp return Win32ResourceForm.COFF; else return Win32ResourceForm.UNKNOWN; } internal Cci.ResourceSection MakeWin32ResourcesFromCOFF(Stream win32Resources, DiagnosticBag diagnostics) { if (win32Resources == null) { return null; } Cci.ResourceSection resources; try { resources = COFFResourceReader.ReadWin32ResourcesFromCOFF(win32Resources); } catch (BadImageFormatException ex) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_BadWin32Resource, Location.None, ex.Message)); return null; } catch (IOException ex) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_BadWin32Resource, Location.None, ex.Message)); return null; } catch (ResourceException ex) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_BadWin32Resource, Location.None, ex.Message)); return null; } return resources; } internal List MakeWin32ResourceList(Stream win32Resources, DiagnosticBag diagnostics) { if (win32Resources == null) { return null; } List resources; try { resources = CvtResFile.ReadResFile(win32Resources); } catch (ResourceException ex) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_BadWin32Resource, Location.None, ex.Message)); return null; } if (resources == null) { return null; } var resourceList = new List(); foreach (var r in resources) { var result = new Win32Resource( data: r.data, codePage: 0, languageId: r.LanguageId, //EDMAURER converting to int from ushort. //Go to short first to avoid sign extension. id: unchecked((short)r.pstringName.Ordinal), name: r.pstringName.theString, typeId: unchecked((short)r.pstringType.Ordinal), typeName: r.pstringType.theString ); resourceList.Add(result); } return resourceList; } internal void SetupWin32Resources(CommonPEModuleBuilder moduleBeingBuilt, Stream win32Resources, DiagnosticBag diagnostics) { if (win32Resources == null) return; Win32ResourceForm resourceForm; try { resourceForm = DetectWin32ResourceForm(win32Resources); } catch (EndOfStreamException) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_BadWin32Resource, NoLocation.Singleton, CodeAnalysisResources.UnrecognizedResourceFileFormat)); return; } catch (Exception ex) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_BadWin32Resource, NoLocation.Singleton, ex.Message)); return; } switch (resourceForm) { case Win32ResourceForm.COFF: moduleBeingBuilt.Win32ResourceSection = MakeWin32ResourcesFromCOFF(win32Resources, diagnostics); break; case Win32ResourceForm.RES: moduleBeingBuilt.Win32Resources = MakeWin32ResourceList(win32Resources, diagnostics); break; default: diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_BadWin32Resource, NoLocation.Singleton, CodeAnalysisResources.UnrecognizedResourceFileFormat)); break; } } internal void ReportManifestResourceDuplicates( IEnumerable manifestResources, IEnumerable addedModuleNames, IEnumerable addedModuleResourceNames, DiagnosticBag diagnostics) { if (Options.OutputKind == OutputKind.NetModule && !(manifestResources != null && manifestResources.Any())) { return; } var uniqueResourceNames = new HashSet(); if (manifestResources != null && manifestResources.Any()) { var uniqueFileNames = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var resource in manifestResources) { if (!uniqueResourceNames.Add(resource.ResourceName)) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_ResourceNotUnique, Location.None, resource.ResourceName)); } // file name could be null if resource is embedded var fileName = resource.FileName; if (fileName != null && !uniqueFileNames.Add(fileName)) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_ResourceFileNameNotUnique, Location.None, fileName)); } } foreach (var fileName in addedModuleNames) { if (!uniqueFileNames.Add(fileName)) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_ResourceFileNameNotUnique, Location.None, fileName)); } } } if (Options.OutputKind != OutputKind.NetModule) { foreach (string name in addedModuleResourceNames) { if (!uniqueResourceNames.Add(name)) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_ResourceNotUnique, Location.None, name)); } } } } #endregion #region Emit /// /// Constructs the module serialization properties out of the compilation options of this compilation. /// internal Cci.ModulePropertiesForSerialization ConstructModuleSerializationProperties( EmitOptions emitOptions, string targetRuntimeVersion, Guid moduleVersionId = default(Guid)) { CompilationOptions compilationOptions = this.Options; Platform platform = compilationOptions.Platform; OutputKind outputKind = compilationOptions.OutputKind; if (!platform.IsValid()) { platform = Platform.AnyCpu; } if (!outputKind.IsValid()) { outputKind = OutputKind.DynamicallyLinkedLibrary; } bool requires64Bit = platform.Requires64Bit(); bool requires32Bit = platform.Requires32Bit(); ushort fileAlignment; if (emitOptions.FileAlignment == 0 || !CompilationOptions.IsValidFileAlignment(emitOptions.FileAlignment)) { fileAlignment = requires64Bit ? Cci.ModulePropertiesForSerialization.DefaultFileAlignment64Bit : Cci.ModulePropertiesForSerialization.DefaultFileAlignment32Bit; } else { fileAlignment = (ushort)emitOptions.FileAlignment; } ulong baseAddress = unchecked(emitOptions.BaseAddress + 0x8000) & (requires64Bit ? 0xffffffffffff0000 : 0x00000000ffff0000); // cover values smaller than 0x8000, overflow and default value 0): if (baseAddress == 0) { if (outputKind == OutputKind.ConsoleApplication || outputKind == OutputKind.WindowsApplication || outputKind == OutputKind.WindowsRuntimeApplication) { baseAddress = (requires64Bit) ? Cci.ModulePropertiesForSerialization.DefaultExeBaseAddress64Bit : Cci.ModulePropertiesForSerialization.DefaultExeBaseAddress32Bit; } else { baseAddress = (requires64Bit) ? Cci.ModulePropertiesForSerialization.DefaultDllBaseAddress64Bit : Cci.ModulePropertiesForSerialization.DefaultDllBaseAddress32Bit; } } ulong sizeOfHeapCommit = requires64Bit ? Cci.ModulePropertiesForSerialization.DefaultSizeOfHeapCommit64Bit : Cci.ModulePropertiesForSerialization.DefaultSizeOfHeapCommit32Bit; // Dev10 always uses the default value for 32bit for sizeOfHeapReserve. // check with link -dump -headers const ulong sizeOfHeapReserve = Cci.ModulePropertiesForSerialization.DefaultSizeOfHeapReserve32Bit; ulong sizeOfStackReserve = requires64Bit ? Cci.ModulePropertiesForSerialization.DefaultSizeOfStackReserve64Bit : Cci.ModulePropertiesForSerialization.DefaultSizeOfStackReserve32Bit; ulong sizeOfStackCommit = requires64Bit ? Cci.ModulePropertiesForSerialization.DefaultSizeOfStackCommit64Bit : Cci.ModulePropertiesForSerialization.DefaultSizeOfStackCommit32Bit; SubsystemVersion subsystemVersion; if (emitOptions.SubsystemVersion.Equals(SubsystemVersion.None) || !emitOptions.SubsystemVersion.IsValid) { subsystemVersion = SubsystemVersion.Default(outputKind, platform); } else { subsystemVersion = emitOptions.SubsystemVersion; } Machine machine; switch (platform) { case Platform.Arm: machine = Machine.ArmThumb2; break; case Platform.X64: machine = Machine.Amd64; break; case Platform.Itanium: machine = Machine.IA64; break; case Platform.X86: machine = Machine.I386; break; case Platform.AnyCpu: case Platform.AnyCpu32BitPreferred: machine = Machine.Unknown; break; default: throw ExceptionUtilities.UnexpectedValue(platform); } return new Cci.ModulePropertiesForSerialization( persistentIdentifier: moduleVersionId, corFlags: GetCorHeaderFlags(machine, HasStrongName, prefers32Bit: platform == Platform.AnyCpu32BitPreferred), fileAlignment: fileAlignment, sectionAlignment: Cci.ModulePropertiesForSerialization.DefaultSectionAlignment, targetRuntimeVersion: targetRuntimeVersion, machine: machine, baseAddress: baseAddress, sizeOfHeapReserve: sizeOfHeapReserve, sizeOfHeapCommit: sizeOfHeapCommit, sizeOfStackReserve: sizeOfStackReserve, sizeOfStackCommit: sizeOfStackCommit, dllCharacteristics: GetDllCharacteristics(emitOptions.HighEntropyVirtualAddressSpace, compilationOptions.OutputKind == OutputKind.WindowsRuntimeApplication), imageCharacteristics: GetCharacteristics(outputKind, requires32Bit), subsystem: GetSubsystem(outputKind), majorSubsystemVersion: (ushort)subsystemVersion.Major, minorSubsystemVersion: (ushort)subsystemVersion.Minor, linkerMajorVersion: this.LinkerMajorVersion, linkerMinorVersion: 0); } private static CorFlags GetCorHeaderFlags(Machine machine, bool strongNameSigned, bool prefers32Bit) { CorFlags result = CorFlags.ILOnly; if (machine == Machine.I386) { result |= CorFlags.Requires32Bit; } if (strongNameSigned) { result |= CorFlags.StrongNameSigned; } if (prefers32Bit) { result |= CorFlags.Requires32Bit | CorFlags.Prefers32Bit; } return result; } internal static DllCharacteristics GetDllCharacteristics(bool enableHighEntropyVA, bool configureToExecuteInAppContainer) { var result = DllCharacteristics.DynamicBase | DllCharacteristics.NxCompatible | DllCharacteristics.NoSeh | DllCharacteristics.TerminalServerAware; if (enableHighEntropyVA) { // IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA result |= (DllCharacteristics)0x0020; } if (configureToExecuteInAppContainer) { result |= DllCharacteristics.AppContainer; } return result; } private static Characteristics GetCharacteristics(OutputKind outputKind, bool requires32Bit) { var characteristics = Characteristics.ExecutableImage; if (requires32Bit) { // 32 bit machine (The standard says to always set this, the linker team says otherwise) // The loader team says that this is not used for anything in the OS. characteristics |= Characteristics.Bit32Machine; } else { // Large address aware (the standard says never to set this, the linker team says otherwise). // The loader team says that this is not overridden for managed binaries and will be respected if set. characteristics |= Characteristics.LargeAddressAware; } switch (outputKind) { case OutputKind.WindowsRuntimeMetadata: case OutputKind.DynamicallyLinkedLibrary: case OutputKind.NetModule: characteristics |= Characteristics.Dll; break; case OutputKind.ConsoleApplication: case OutputKind.WindowsRuntimeApplication: case OutputKind.WindowsApplication: break; default: throw ExceptionUtilities.UnexpectedValue(outputKind); } return characteristics; } private static Subsystem GetSubsystem(OutputKind outputKind) { switch (outputKind) { case OutputKind.ConsoleApplication: case OutputKind.DynamicallyLinkedLibrary: case OutputKind.NetModule: case OutputKind.WindowsRuntimeMetadata: return Subsystem.WindowsCui; case OutputKind.WindowsRuntimeApplication: case OutputKind.WindowsApplication: return Subsystem.WindowsGui; default: throw ExceptionUtilities.UnexpectedValue(outputKind); } } /// /// The value is not used by Windows loader, but the OS appcompat infrastructure uses it to identify apps. /// It is useful for us to have a mechanism to identify the compiler that produced the binary. /// This is the appropriate value to use for that. That is what it was invented for. /// We don't want to have the high bit set for this in case some users perform a signed comparison to /// determine if the value is less than some version. The C++ linker is at 0x0B. /// We'll start our numbering at 0x30 for C#, 0x50 for VB. /// internal abstract byte LinkerMajorVersion { get; } internal bool HasStrongName { get { return !IsDelaySigned && Options.OutputKind != OutputKind.NetModule && StrongNameKeys.CanProvideStrongName; } } internal bool IsRealSigned { get { // A module cannot be signed. The native compiler allowed one to create a netmodule with an AssemblyKeyFile // or Container attribute (or specify a key via the cmd line). When the module was linked into an assembly, // alink would sign the assembly. So rather than give an error we just don't sign when outputting a module. return !IsDelaySigned && !Options.PublicSign && Options.OutputKind != OutputKind.NetModule && StrongNameKeys.CanSign; } } /// /// Return true if the compilation contains any code or types. /// internal abstract bool HasCodeToEmit(); internal abstract bool IsDelaySigned { get; } internal abstract StrongNameKeys StrongNameKeys { get; } internal abstract CommonPEModuleBuilder CreateModuleBuilder( EmitOptions emitOptions, IMethodSymbol debugEntryPoint, Stream sourceLinkStream, IEnumerable embeddedTexts, IEnumerable manifestResources, CompilationTestData testData, DiagnosticBag diagnostics, CancellationToken cancellationToken); /// /// Report declaration diagnostics and compile and synthesize method bodies. /// /// True if successful. internal abstract bool CompileMethods( CommonPEModuleBuilder moduleBuilder, bool emittingPdb, bool emitMetadataOnly, bool emitTestCoverageData, DiagnosticBag diagnostics, Predicate filterOpt, CancellationToken cancellationToken); internal bool CreateDebugDocuments(DebugDocumentsBuilder documentsBuilder, IEnumerable embeddedTexts, DiagnosticBag diagnostics) { // Check that all syntax trees are debuggable: bool allTreesDebuggable = true; foreach (var tree in SyntaxTrees) { if (!string.IsNullOrEmpty(tree.FilePath) && tree.GetText().Encoding == null) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_EncodinglessSyntaxTree, tree.GetRoot().GetLocation())); allTreesDebuggable = false; } } if (!allTreesDebuggable) { return false; } // Add debug documents for all embedded text first. This ensures that embedding // takes priority over the syntax tree pass, which will not embed. if (!embeddedTexts.IsEmpty()) { var embeddedDocuments = ArrayBuilder.GetInstance(); foreach (var text in embeddedTexts) { Debug.Assert(!string.IsNullOrEmpty(text.FilePath)); string normalizedPath = documentsBuilder.NormalizeDebugDocumentPath(text.FilePath, basePath: null); var existingDoc = documentsBuilder.TryGetDebugDocumentForNormalizedPath(normalizedPath); if (existingDoc == null) { var document = new Cci.DebugSourceDocument( normalizedPath, DebugSourceDocumentLanguageId, () => text.GetDebugSourceInfo()); documentsBuilder.AddDebugDocument(document); embeddedDocuments.Add(document); } } documentsBuilder.EmbeddedDocuments = embeddedDocuments.ToImmutableAndFree(); } // Add debug documents for all trees with distinct paths. foreach (var tree in SyntaxTrees) { if (!string.IsNullOrEmpty(tree.FilePath)) { // compilation does not guarantee that all trees will have distinct paths. // Do not attempt adding a document for a particular path if we already added one. string normalizedPath = documentsBuilder.NormalizeDebugDocumentPath(tree.FilePath, basePath: null); var existingDoc = documentsBuilder.TryGetDebugDocumentForNormalizedPath(normalizedPath); if (existingDoc == null) { documentsBuilder.AddDebugDocument(new Cci.DebugSourceDocument( normalizedPath, DebugSourceDocumentLanguageId, () => tree.GetDebugSourceInfo())); } } } // Add debug documents for all pragmas. // If there are clashes with already processed directives, report warnings. // If there are clashes with debug documents that came from actual trees, ignore the pragma. // Therefore we need to add these in a separate pass after documents for syntax trees were added. foreach (var tree in SyntaxTrees) { AddDebugSourceDocumentsForChecksumDirectives(documentsBuilder, tree, diagnostics); } return true; } internal abstract Guid DebugSourceDocumentLanguageId { get; } internal abstract void AddDebugSourceDocumentsForChecksumDirectives(DebugDocumentsBuilder documentsBuilder, SyntaxTree tree, DiagnosticBag diagnostics); /// /// Update resources and generate XML documentation comments. /// /// True if successful. internal abstract bool GenerateResourcesAndDocumentationComments( CommonPEModuleBuilder moduleBeingBuilt, Stream xmlDocumentationStream, Stream win32ResourcesStream, string outputNameOverride, DiagnosticBag diagnostics, CancellationToken cancellationToken); /// /// Reports all unused imports/usings so far (and thus it must be called as a last step of Emit) /// internal abstract void ReportUnusedImports( SyntaxTree filterTree, DiagnosticBag diagnostics, CancellationToken cancellationToken); /// /// Signals the event queue, if any, that we are done compiling. /// There should not be more compiling actions after this step. /// NOTE: once we signal about completion to analyzers they will cancel and thus in some cases we /// may be effectively cutting off some diagnostics. /// It is not clear if behavior is desirable. /// See: https://github.com/dotnet/roslyn/issues/11470 /// /// What tree to complete. null means complete all trees. internal abstract void CompleteTrees(SyntaxTree filterTree); internal bool Compile( CommonPEModuleBuilder moduleBuilder, bool emittingPdb, DiagnosticBag diagnostics, Predicate filterOpt, CancellationToken cancellationToken) { try { return CompileMethods( moduleBuilder, emittingPdb, emitMetadataOnly: false, emitTestCoverageData: false, diagnostics: diagnostics, filterOpt: filterOpt, cancellationToken: cancellationToken); } finally { moduleBuilder.CompilationFinished(); } } internal void EnsureAnonymousTypeTemplates(CancellationToken cancellationToken) { Debug.Assert(IsSubmission); if (this.GetSubmissionSlotIndex() >= 0 && HasCodeToEmit()) { if (!this.CommonAnonymousTypeManager.AreTemplatesSealed) { var discardedDiagnostics = DiagnosticBag.GetInstance(); var moduleBeingBuilt = this.CreateModuleBuilder( emitOptions: EmitOptions.Default, debugEntryPoint: null, manifestResources: null, sourceLinkStream: null, embeddedTexts: null, testData: null, diagnostics: discardedDiagnostics, cancellationToken: cancellationToken); if (moduleBeingBuilt != null) { Compile( moduleBeingBuilt, diagnostics: discardedDiagnostics, emittingPdb: false, filterOpt: null, cancellationToken: cancellationToken); } discardedDiagnostics.Free(); } Debug.Assert(this.CommonAnonymousTypeManager.AreTemplatesSealed); } else { this.ScriptCompilationInfo.PreviousScriptCompilation?.EnsureAnonymousTypeTemplates(cancellationToken); } } // 1.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH [EditorBrowsable(EditorBrowsableState.Never)] public EmitResult Emit( Stream peStream, Stream pdbStream, Stream xmlDocumentationStream, Stream win32Resources, IEnumerable manifestResources, EmitOptions options, CancellationToken cancellationToken) { return Emit( peStream, pdbStream, xmlDocumentationStream, win32Resources, manifestResources, options, default(IMethodSymbol), default(Stream), default(IEnumerable), cancellationToken); } // 1.3 BACKCOMPAT OVERLOAD -- DO NOT TOUCH [EditorBrowsable(EditorBrowsableState.Never)] public EmitResult Emit( Stream peStream, Stream pdbStream, Stream xmlDocumentationStream, Stream win32Resources, IEnumerable manifestResources, EmitOptions options, IMethodSymbol debugEntryPoint, CancellationToken cancellationToken) { return Emit( peStream, pdbStream, xmlDocumentationStream, win32Resources, manifestResources, options, debugEntryPoint, default(Stream), default(IEnumerable), cancellationToken); } // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH public EmitResult Emit( Stream peStream, Stream pdbStream, Stream xmlDocumentationStream, Stream win32Resources, IEnumerable manifestResources, EmitOptions options, IMethodSymbol debugEntryPoint, Stream sourceLinkStream, IEnumerable embeddedTexts, CancellationToken cancellationToken) { return Emit( peStream, pdbStream, xmlDocumentationStream, win32Resources, manifestResources, options, debugEntryPoint, sourceLinkStream, embeddedTexts, metadataPEStream: null, cancellationToken: cancellationToken); } /// /// Emit the IL for the compiled source code into the specified stream. /// /// Stream to which the compilation will be written. /// Stream to which the metadata-only output will be written. /// Stream to which the compilation's debug info will be written. Null to forego PDB generation. /// Stream to which the compilation's XML documentation will be written. Null to forego XML generation. /// Stream from which the compilation's Win32 resources will be read (in RES format). /// Null to indicate that there are none. The RES format begins with a null resource entry. /// List of the compilation's managed resources. Null to indicate that there are none. /// Emit options. /// /// Debug entry-point of the assembly. The method token is stored in the generated PDB stream. /// /// When a program launches with a debugger attached the debugger places the first breakpoint to the start of the debug entry-point method. /// The CLR starts executing the static Main method of type. When the first breakpoint is hit /// the debugger steps thru the code statement by statement until user code is reached, skipping methods marked by , /// and taking other debugging attributes into consideration. /// /// By default both entry points in an executable program (, , ) /// are the same method (Main). A non-executable program has no entry point. Runtimes that implement a custom loader may specify debug entry-point /// to force the debugger to skip over complex custom loader logic executing at the beginning of the .exe and thus improve debugging experience. /// /// Unlike ordinary entry-point which is limited to a non-generic static method of specific signature, there are no restrictions on the /// method other than having a method body (extern, interface, or abstract methods are not allowed). /// /// /// Stream containing information linking the compilation to a source control. /// /// /// Texts to embed in the PDB. /// Only supported when emitting Portable PDBs. /// /// To cancel the emit process. public EmitResult Emit( Stream peStream, Stream pdbStream = null, Stream xmlDocumentationStream = null, Stream win32Resources = null, IEnumerable manifestResources = null, EmitOptions options = null, IMethodSymbol debugEntryPoint = null, Stream sourceLinkStream = null, IEnumerable embeddedTexts = null, Stream metadataPEStream = null, CancellationToken cancellationToken = default(CancellationToken)) { if (peStream == null) { throw new ArgumentNullException(nameof(peStream)); } if (!peStream.CanWrite) { throw new ArgumentException(CodeAnalysisResources.StreamMustSupportWrite, nameof(peStream)); } if (pdbStream != null) { if (options?.DebugInformationFormat == DebugInformationFormat.Embedded) { throw new ArgumentException(CodeAnalysisResources.PdbStreamUnexpectedWhenEmbedding, nameof(pdbStream)); } if (!pdbStream.CanWrite) { throw new ArgumentException(CodeAnalysisResources.StreamMustSupportWrite, nameof(pdbStream)); } if (options?.EmitMetadataOnly == true) { throw new ArgumentException(CodeAnalysisResources.PdbStreamUnexpectedWhenEmittingMetadataOnly, nameof(pdbStream)); } } if (metadataPEStream != null && options?.EmitMetadataOnly == true) { throw new ArgumentException(CodeAnalysisResources.MetadataPeStreamUnexpectedWhenEmittingMetadataOnly, nameof(metadataPEStream)); } if (metadataPEStream != null && options?.IncludePrivateMembers == true) { throw new ArgumentException(CodeAnalysisResources.IncludingPrivateMembersUnexpectedWhenEmittingToMetadataPeStream, nameof(metadataPEStream)); } if (metadataPEStream == null && options?.EmitMetadataOnly == false && options?.IncludePrivateMembers == false) { throw new ArgumentException(CodeAnalysisResources.MustIncludePrivateMembersUnlessRefAssembly, nameof(options.IncludePrivateMembers)); } if (options?.DebugInformationFormat == DebugInformationFormat.Embedded && options?.EmitMetadataOnly == true) { throw new ArgumentException(CodeAnalysisResources.EmbeddingPdbUnexpectedWhenEmittingMetadata, nameof(metadataPEStream)); } if (this.Options.OutputKind == OutputKind.NetModule) { if (metadataPEStream != null) { throw new ArgumentException(CodeAnalysisResources.CannotTargetNetModuleWhenEmittingRefAssembly, nameof(metadataPEStream)); } else if (options?.EmitMetadataOnly == true) { throw new ArgumentException(CodeAnalysisResources.CannotTargetNetModuleWhenEmittingRefAssembly, nameof(options.EmitMetadataOnly)); } } if (win32Resources != null) { if (!win32Resources.CanRead || !win32Resources.CanSeek) { throw new ArgumentException(CodeAnalysisResources.StreamMustSupportReadAndSeek, nameof(win32Resources)); } } if (sourceLinkStream != null && !sourceLinkStream.CanRead) { throw new ArgumentException(CodeAnalysisResources.StreamMustSupportRead, nameof(sourceLinkStream)); } if (embeddedTexts != null && !embeddedTexts.IsEmpty()) { if (options == null || options.DebugInformationFormat == DebugInformationFormat.Pdb || options.DebugInformationFormat == DebugInformationFormat.PortablePdb && pdbStream == null) { throw new ArgumentException(CodeAnalysisResources.EmbeddedTextsRequirePortablePdb, nameof(embeddedTexts)); } } return Emit( peStream, metadataPEStream, pdbStream, xmlDocumentationStream, win32Resources, manifestResources, options, debugEntryPoint, sourceLinkStream, embeddedTexts, testData: null, cancellationToken: cancellationToken); } /// /// This overload is only intended to be directly called by tests that want to pass . /// The map is used for storing a list of methods and their associated IL. /// internal EmitResult Emit( Stream peStream, Stream metadataPEStream, Stream pdbStream, Stream xmlDocumentationStream, Stream win32Resources, IEnumerable manifestResources, EmitOptions options, IMethodSymbol debugEntryPoint, Stream sourceLinkStream, IEnumerable embeddedTexts, CompilationTestData testData, CancellationToken cancellationToken) { options = options ?? EmitOptions.Default.WithIncludePrivateMembers(metadataPEStream == null); bool embedPdb = options.DebugInformationFormat == DebugInformationFormat.Embedded; Debug.Assert(!embedPdb || pdbStream == null); Debug.Assert(metadataPEStream == null || !options.IncludePrivateMembers); // you may not use a secondary stream and include private members together var diagnostics = DiagnosticBag.GetInstance(); var moduleBeingBuilt = CheckOptionsAndCreateModuleBuilder( diagnostics, manifestResources, options, debugEntryPoint, sourceLinkStream, embeddedTexts, testData, cancellationToken); bool success = false; if (moduleBeingBuilt != null) { try { success = CompileMethods( moduleBeingBuilt, emittingPdb: pdbStream != null || embedPdb, emitMetadataOnly: options.EmitMetadataOnly, emitTestCoverageData: options.EmitTestCoverageData, diagnostics: diagnostics, filterOpt: null, cancellationToken: cancellationToken); if (!options.EmitMetadataOnly) { if (!GenerateResourcesAndDocumentationComments( moduleBeingBuilt, xmlDocumentationStream, win32Resources, options.OutputNameOverride, diagnostics, cancellationToken)) { success = false; } if (success) { ReportUnusedImports(null, diagnostics, cancellationToken); } } } finally { moduleBeingBuilt.CompilationFinished(); } if (success) { success = SerializeToPeStream( moduleBeingBuilt, new SimpleEmitStreamProvider(peStream), (metadataPEStream != null) ? new SimpleEmitStreamProvider(metadataPEStream) : null, (pdbStream != null) ? new SimpleEmitStreamProvider(pdbStream) : null, testData?.SymWriterFactory, diagnostics, metadataOnly: options.EmitMetadataOnly, includePrivateMembers: options.IncludePrivateMembers, emitTestCoverageData: options.EmitTestCoverageData, pePdbFilePath: options.PdbFilePath, cancellationToken: cancellationToken); } } return new EmitResult(success, diagnostics.ToReadOnlyAndFree()); } /// /// Emit the differences between the compilation and the previous generation /// for Edit and Continue. The differences are expressed as added and changed /// symbols, and are emitted as metadata, IL, and PDB deltas. A representation /// of the current compilation is returned as an EmitBaseline for use in a /// subsequent Edit and Continue. /// public EmitDifferenceResult EmitDifference( EmitBaseline baseline, IEnumerable edits, Stream metadataStream, Stream ilStream, Stream pdbStream, ICollection updatedMethods, CancellationToken cancellationToken = default(CancellationToken)) { return EmitDifference(baseline, edits, s => false, metadataStream, ilStream, pdbStream, updatedMethods, cancellationToken); } /// /// Emit the differences between the compilation and the previous generation /// for Edit and Continue. The differences are expressed as added and changed /// symbols, and are emitted as metadata, IL, and PDB deltas. A representation /// of the current compilation is returned as an EmitBaseline for use in a /// subsequent Edit and Continue. /// public EmitDifferenceResult EmitDifference( EmitBaseline baseline, IEnumerable edits, Func isAddedSymbol, Stream metadataStream, Stream ilStream, Stream pdbStream, ICollection updatedMethods, CancellationToken cancellationToken = default(CancellationToken)) { if (baseline == null) { throw new ArgumentNullException(nameof(baseline)); } // TODO: check if baseline is an assembly manifest module/netmodule // Do we support EnC on netmodules? if (edits == null) { throw new ArgumentNullException(nameof(edits)); } if (isAddedSymbol == null) { throw new ArgumentNullException(nameof(isAddedSymbol)); } if (metadataStream == null) { throw new ArgumentNullException(nameof(metadataStream)); } if (ilStream == null) { throw new ArgumentNullException(nameof(ilStream)); } if (pdbStream == null) { throw new ArgumentNullException(nameof(pdbStream)); } return this.EmitDifference(baseline, edits, isAddedSymbol, metadataStream, ilStream, pdbStream, updatedMethods, null, cancellationToken); } internal abstract EmitDifferenceResult EmitDifference( EmitBaseline baseline, IEnumerable edits, Func isAddedSymbol, Stream metadataStream, Stream ilStream, Stream pdbStream, ICollection updatedMethodHandles, CompilationTestData testData, CancellationToken cancellationToken); /// /// Check compilation options and create . /// /// if successful. internal CommonPEModuleBuilder CheckOptionsAndCreateModuleBuilder( DiagnosticBag diagnostics, IEnumerable manifestResources, EmitOptions options, IMethodSymbol debugEntryPoint, Stream sourceLinkStream, IEnumerable embeddedTexts, CompilationTestData testData, CancellationToken cancellationToken) { options.ValidateOptions(diagnostics, this.MessageProvider); if (debugEntryPoint != null) { ValidateDebugEntryPoint(debugEntryPoint, diagnostics); } if (Options.OutputKind == OutputKind.NetModule && manifestResources != null) { foreach (ResourceDescription res in manifestResources) { if (res.FileName != null) { // Modules can have only embedded resources, not linked ones. diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_ResourceInModule, Location.None)); } } } if (diagnostics.HasAnyErrors()) { return null; } // Do not waste a slot in the submission chain for submissions that contain no executable code // (they may only contain #r directives, usings, etc.) if (IsSubmission && !HasCodeToEmit()) { // Still report diagnostics since downstream submissions will assume there are no errors. diagnostics.AddRange(this.GetDiagnostics()); return null; } return this.CreateModuleBuilder( options, debugEntryPoint, sourceLinkStream, embeddedTexts, manifestResources, testData, diagnostics, cancellationToken); } internal abstract void ValidateDebugEntryPoint(IMethodSymbol debugEntryPoint, DiagnosticBag diagnostics); internal bool IsEmitDeterministic => this.Options.Deterministic; internal bool SerializeToPeStream( CommonPEModuleBuilder moduleBeingBuilt, EmitStreamProvider peStreamProvider, EmitStreamProvider metadataPEStreamProvider, EmitStreamProvider pdbStreamProvider, Func testSymWriterFactory, DiagnosticBag diagnostics, bool metadataOnly, bool includePrivateMembers, bool emitTestCoverageData, string pePdbFilePath, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); Cci.PdbWriter nativePdbWriter = null; Stream signingInputStream = null; DiagnosticBag metadataDiagnostics = null; DiagnosticBag pdbBag = null; Stream peStream = null; Stream refPeStream = null; Stream portablePdbStream = null; bool deterministic = IsEmitDeterministic; // PDB Stream provider should not be given if PDB is to be embedded into the PE file: Debug.Assert(moduleBeingBuilt.DebugInformationFormat != DebugInformationFormat.Embedded || pdbStreamProvider == null); if (moduleBeingBuilt.DebugInformationFormat == DebugInformationFormat.Embedded || pdbStreamProvider != null) { pePdbFilePath = pePdbFilePath ?? FileNameUtilities.ChangeExtension(SourceModule.Name, "pdb"); } if (moduleBeingBuilt.DebugInformationFormat == DebugInformationFormat.Embedded && !string.IsNullOrEmpty(pePdbFilePath)) { pePdbFilePath = Path.GetFileName(pePdbFilePath); } try { metadataDiagnostics = DiagnosticBag.GetInstance(); if (moduleBeingBuilt.DebugInformationFormat == DebugInformationFormat.Pdb && pdbStreamProvider != null) { // The calls ISymUnmanagedWriter2.GetDebugInfo require a file name in order to succeed. This is // frequently used during PDB writing. Ensure a name is provided here in the case we were given // only a Stream value. nativePdbWriter = new Cci.PdbWriter(pePdbFilePath, testSymWriterFactory, deterministic); } Func getPortablePdbStream; if (moduleBeingBuilt.DebugInformationFormat == DebugInformationFormat.PortablePdb && pdbStreamProvider != null) { getPortablePdbStream = () => { if (metadataDiagnostics.HasAnyErrors()) { return null; } portablePdbStream = pdbStreamProvider.GetOrCreateStream(metadataDiagnostics); Debug.Assert(portablePdbStream != null || metadataDiagnostics.HasAnyErrors()); return portablePdbStream; }; } else { getPortablePdbStream = null; } Func getPeStream = () => { if (metadataDiagnostics.HasAnyErrors()) { return null; } peStream = peStreamProvider.GetOrCreateStream(metadataDiagnostics); if (peStream == null) { Debug.Assert(metadataDiagnostics.HasAnyErrors()); return null; } // Signing can only be done to on-disk files. This is a limitation of the CLR APIs which we use // to perform strong naming. If this binary is configured to be signed, create a temp file, output to that // then stream that to the stream that this method was called with. Otherwise output to the // stream that this method was called with. Stream retStream; if (!metadataOnly && IsRealSigned) { Debug.Assert(Options.StrongNameProvider != null); try { signingInputStream = Options.StrongNameProvider.CreateInputStream(); } catch (IOException e) { throw new Cci.PeWritingException(e); } retStream = signingInputStream; } else { signingInputStream = null; retStream = peStream; } return retStream; }; Func getRefPeStream; if (metadataPEStreamProvider != null) { getRefPeStream = () => { if (metadataDiagnostics.HasAnyErrors()) { return null; } refPeStream = metadataPEStreamProvider.GetOrCreateStream(metadataDiagnostics); Debug.Assert(refPeStream != null || metadataDiagnostics.HasAnyErrors()); return refPeStream; }; } else { getRefPeStream = null; } try { if (SerializePeToStream( moduleBeingBuilt, metadataDiagnostics, this.MessageProvider, getPeStream, getRefPeStream, getPortablePdbStream, nativePdbWriter, pePdbFilePath, metadataOnly, includePrivateMembers, deterministic, emitTestCoverageData, cancellationToken)) { if (nativePdbWriter != null) { var nativePdbStream = pdbStreamProvider.GetOrCreateStream(metadataDiagnostics); Debug.Assert(nativePdbStream != null || metadataDiagnostics.HasAnyErrors()); if (nativePdbStream != null) { nativePdbWriter.WriteTo(nativePdbStream); } } } } catch (Cci.PdbWritingException ex) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_PdbWritingFailed, Location.None, ex.Message)); return false; } catch (Cci.PeWritingException e) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_PeWritingFailure, Location.None, e.InnerException.ToString())); return false; } catch (ResourceException e) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_CantReadResource, Location.None, e.Message, e.InnerException.Message)); return false; } catch (PermissionSetFileReadException e) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_PermissionSetAttributeFileReadError, Location.None, e.FileName, e.PropertyName, e.Message)); return false; } // translate metadata errors. if (!FilterAndAppendAndFreeDiagnostics(diagnostics, ref metadataDiagnostics)) { return false; } if (signingInputStream != null && peStream != null) { Debug.Assert(Options.StrongNameProvider != null); try { Options.StrongNameProvider.SignAssembly(StrongNameKeys, signingInputStream, peStream); } catch (DesktopStrongNameProvider.ClrStrongNameMissingException) { diagnostics.Add(StrongNameKeys.GetError(StrongNameKeys.KeyFilePath, StrongNameKeys.KeyContainer, new CodeAnalysisResourcesLocalizableErrorArgument(nameof(CodeAnalysisResources.AssemblySigningNotSupported)), MessageProvider)); return false; } catch (IOException ex) { diagnostics.Add(StrongNameKeys.GetError(StrongNameKeys.KeyFilePath, StrongNameKeys.KeyContainer, ex.Message, MessageProvider)); return false; } } } finally { nativePdbWriter?.Dispose(); signingInputStream?.Dispose(); pdbBag?.Free(); metadataDiagnostics?.Free(); } return true; } internal static bool SerializePeToStream( CommonPEModuleBuilder moduleBeingBuilt, DiagnosticBag metadataDiagnostics, CommonMessageProvider messageProvider, Func getPeStream, Func getMetadataPeStreamOpt, Func getPortablePdbStreamOpt, Cci.PdbWriter nativePdbWriterOpt, string pdbPathOpt, bool metadataOnly, bool includePrivateMembers, bool isDeterministic, bool emitTestCoverageData, CancellationToken cancellationToken) { bool emitSecondaryAssembly = getMetadataPeStreamOpt != null; bool includePrivateMembersOnPrimaryOutput = metadataOnly ? includePrivateMembers : true; bool deterministicPrimaryOutput = (metadataOnly && !includePrivateMembers) || isDeterministic; if (!Cci.PeWriter.WritePeToStream( new EmitContext(moduleBeingBuilt, null, metadataDiagnostics, metadataOnly, includePrivateMembersOnPrimaryOutput), messageProvider, getPeStream, getPortablePdbStreamOpt, nativePdbWriterOpt, pdbPathOpt, metadataOnly, deterministicPrimaryOutput, emitTestCoverageData, cancellationToken)) { return false; } // produce the secondary output (ref assembly) if needed if (emitSecondaryAssembly) { Debug.Assert(!metadataOnly); Debug.Assert(!includePrivateMembers); if (!Cci.PeWriter.WritePeToStream( new EmitContext(moduleBeingBuilt, null, metadataDiagnostics, metadataOnly: true, includePrivateMembers: false), messageProvider, getMetadataPeStreamOpt, getPortablePdbStreamOpt: null, nativePdbWriterOpt: null, pdbPathOpt: null, metadataOnly: true, isDeterministic: true, emitTestCoverageData: false, cancellationToken: cancellationToken)) { return false; } } return true; } internal EmitBaseline SerializeToDeltaStreams( CommonPEModuleBuilder moduleBeingBuilt, EmitBaseline baseline, DefinitionMap definitionMap, SymbolChanges changes, Stream metadataStream, Stream ilStream, Stream pdbStream, ICollection updatedMethods, DiagnosticBag diagnostics, Func testSymWriterFactory, string pdbFilePath, CancellationToken cancellationToken) { var nativePdbWriterOpt = (moduleBeingBuilt.DebugInformationFormat != DebugInformationFormat.Pdb) ? null : new Cci.PdbWriter( pdbFilePath ?? FileNameUtilities.ChangeExtension(SourceModule.Name, "pdb"), testSymWriterFactory, deterministic: false); using (nativePdbWriterOpt) { var context = new EmitContext(moduleBeingBuilt, null, diagnostics, metadataOnly: false, includePrivateMembers: true); var encId = Guid.NewGuid(); try { var writer = new DeltaMetadataWriter( context, MessageProvider, baseline, encId, definitionMap, changes, cancellationToken); writer.WriteMetadataAndIL( nativePdbWriterOpt, metadataStream, ilStream, (nativePdbWriterOpt == null) ? pdbStream : null, out MetadataSizes metadataSizes); writer.GetMethodTokens(updatedMethods); nativePdbWriterOpt?.WriteTo(pdbStream); return diagnostics.HasAnyErrors() ? null : writer.GetDelta(baseline, this, encId, metadataSizes); } catch (Cci.PdbWritingException e) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_PdbWritingFailed, Location.None, e.Message)); return null; } catch (Cci.PeWritingException e) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_PeWritingFailure, Location.None, e.InnerException.ToString())); return null; } catch (PermissionSetFileReadException e) { diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_PermissionSetAttributeFileReadError, Location.None, e.FileName, e.PropertyName, e.Message)); return null; } } } internal string Feature(string p) { string v; return _features.TryGetValue(p, out v) ? v : null; } #endregion private ConcurrentDictionary _lazyTreeToUsedImportDirectivesMap; private static readonly Func s_createSetCallback = t => new SmallConcurrentSetOfInts(); private ConcurrentDictionary TreeToUsedImportDirectivesMap { get { return LazyInitializer.EnsureInitialized(ref _lazyTreeToUsedImportDirectivesMap); } } internal void MarkImportDirectiveAsUsed(SyntaxNode node) { MarkImportDirectiveAsUsed(node.SyntaxTree, node.Span.Start); } internal void MarkImportDirectiveAsUsed(SyntaxTree syntaxTree, int position) { // Optimization: Don't initialize TreeToUsedImportDirectivesMap in submissions. if (!IsSubmission && syntaxTree != null) { var set = TreeToUsedImportDirectivesMap.GetOrAdd(syntaxTree, s_createSetCallback); set.Add(position); } } internal bool IsImportDirectiveUsed(SyntaxTree syntaxTree, int position) { if (IsSubmission) { // Since usings apply to subsequent submissions, we have to assume they are used. return true; } SmallConcurrentSetOfInts usedImports; return syntaxTree != null && TreeToUsedImportDirectivesMap.TryGetValue(syntaxTree, out usedImports) && usedImports.Contains(position); } /// /// The compiler needs to define an ordering among different partial class in different syntax trees /// in some cases, because emit order for fields in structures, for example, is semantically important. /// This function defines an ordering among syntax trees in this compilation. /// internal int CompareSyntaxTreeOrdering(SyntaxTree tree1, SyntaxTree tree2) { if (tree1 == tree2) { return 0; } Debug.Assert(this.ContainsSyntaxTree(tree1)); Debug.Assert(this.ContainsSyntaxTree(tree2)); return this.GetSyntaxTreeOrdinal(tree1) - this.GetSyntaxTreeOrdinal(tree2); } internal abstract int GetSyntaxTreeOrdinal(SyntaxTree tree); /// /// Compare two source locations, using their containing trees, and then by Span.First within a tree. /// Can be used to get a total ordering on declarations, for example. /// internal abstract int CompareSourceLocations(Location loc1, Location loc2); /// /// Return the lexically first of two locations. /// internal TLocation FirstSourceLocation(TLocation first, TLocation second) where TLocation : Location { if (CompareSourceLocations(first, second) <= 0) { return first; } else { return second; } } /// /// Return the lexically first of multiple locations. /// internal TLocation FirstSourceLocation(ImmutableArray locations) where TLocation : Location { if (locations.IsEmpty) { return null; } var result = locations[0]; for (int i = 1; i < locations.Length; i++) { result = FirstSourceLocation(result, locations[i]); } return result; } #region Logging Helpers // Following helpers are used when logging ETW events. These helpers are invoked only if we are running // under an ETW listener that has requested 'verbose' logging. In other words, these helpers will never // be invoked in the 'normal' case (i.e. when the code is running on user's machine and no ETW listener // is involved). // Note: Most of the below helpers are unused at the moment - but we would like to keep them around in // case we decide we need more verbose logging in certain cases for debugging. internal string GetMessage(CompilationStage stage) { return string.Format("{0} ({1})", this.AssemblyName, stage.ToString()); } internal string GetMessage(ITypeSymbol source, ITypeSymbol destination) { if (source == null || destination == null) return this.AssemblyName; return string.Format("{0}: {1} {2} -> {3} {4}", this.AssemblyName, source.TypeKind.ToString(), source.Name, destination.TypeKind.ToString(), destination.Name); } #endregion #region Declaration Name Queries /// /// Return true if there is a source declaration symbol name that meets given predicate. /// public abstract bool ContainsSymbolsWithName(Func predicate, SymbolFilter filter = SymbolFilter.TypeAndMember, CancellationToken cancellationToken = default(CancellationToken)); /// /// Return source declaration symbols whose name meets given predicate. /// public abstract IEnumerable GetSymbolsWithName(Func predicate, SymbolFilter filter = SymbolFilter.TypeAndMember, CancellationToken cancellationToken = default(CancellationToken)); #endregion internal void MakeMemberMissing(WellKnownMember member) { MakeMemberMissing((int)member); } internal void MakeMemberMissing(SpecialMember member) { MakeMemberMissing(-(int)member - 1); } internal bool IsMemberMissing(WellKnownMember member) { return IsMemberMissing((int)member); } internal bool IsMemberMissing(SpecialMember member) { return IsMemberMissing(-(int)member - 1); } private void MakeMemberMissing(int member) { if (_lazyMakeMemberMissingMap == null) { _lazyMakeMemberMissingMap = new SmallDictionary(); } _lazyMakeMemberMissingMap[member] = true; } private bool IsMemberMissing(int member) { return _lazyMakeMemberMissingMap != null && _lazyMakeMemberMissingMap.ContainsKey(member); } internal void MakeTypeMissing(WellKnownType type) { if (_lazyMakeWellKnownTypeMissingMap == null) { _lazyMakeWellKnownTypeMissingMap = new SmallDictionary(); } _lazyMakeWellKnownTypeMissingMap[(int)type] = true; } internal bool IsTypeMissing(WellKnownType type) { return _lazyMakeWellKnownTypeMissingMap != null && _lazyMakeWellKnownTypeMissingMap.ContainsKey((int)type); } internal abstract bool IsIOperationFeatureEnabled(); /// /// Given a reporting unreferenced s, returns /// the actual instances that were not referenced. /// public ImmutableArray GetUnreferencedAssemblyIdentities(Diagnostic diagnostic) { if (diagnostic == null) { throw new ArgumentNullException(nameof(diagnostic)); } if (!IsUnreferencedAssemblyIdentityDiagnosticCode(diagnostic.Code)) { return ImmutableArray.Empty; } var builder = ArrayBuilder.GetInstance(); foreach (var argument in diagnostic.Arguments) { if (argument is AssemblyIdentity id) { builder.Add(id); } } return builder.ToImmutableAndFree(); } internal abstract bool IsUnreferencedAssemblyIdentityDiagnosticCode(int code); /// /// Returns the required language version found in a , if any is found. /// Returns null if none is found. /// public static string GetRequiredLanguageVersion(Diagnostic diagnostic) { if (diagnostic == null) { throw new ArgumentNullException(nameof(diagnostic)); } bool found = false; string foundVersion = null; if (diagnostic.Arguments != null) { foreach (var argument in diagnostic.Arguments) { if (argument is RequiredLanguageVersion versionDiagnostic) { Debug.Assert(!found); // only one required language version in a given diagnostic found = true; foundVersion = versionDiagnostic.ToString(); } } } return foundVersion; } } }