// 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.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("inconsistent syntax tree features", 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 abstract INamedTypeSymbol CreateErrorTypeSymbol(INamespaceOrTypeSymbol container, string name, int arity);
#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 static void CheckAssemblyName(string assemblyName)
{
// We could only allow name == null if OutputKind is Module.
// However we couldn't check such condition here since one wouldn't be able to call WithName(...).WithOptions(...).
// It does no harm that we allow name == null for assemblies as well, so we don't enforce it.
if (assemblyName != null)
{
MetadataHelpers.ValidateAssemblyOrModuleName(assemblyName, "assemblyName");
}
}
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("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("Reference of type '{0}' is not valid for this compilation.", reference.GetType()), "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, 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($"MetadataReference '{r}' not found to remove", 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; }
///
/// 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);
///
/// Returns a new INamedTypeSymbol with the given element types and (optional) element names.
///
///
/// Since VB doesn't support tuples yet, this call will fail in a VB compilation.
///
public INamedTypeSymbol CreateTupleTypeSymbol(ImmutableArray elementTypes, ImmutableArray elementNames = default(ImmutableArray))
{
return CommonCreateTupleTypeSymbol(elementTypes, elementNames);
}
protected abstract INamedTypeSymbol CommonCreateTupleTypeSymbol(ImmutableArray elementTypes, ImmutableArray elementNames);
///
/// 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))
{
return CommonCreateTupleTypeSymbol(underlyingType, elementNames);
}
protected abstract INamedTypeSymbol CommonCreateTupleTypeSymbol(INamedTypeSymbol underlyingType, ImmutableArray elementNames);
///
/// Returns a new anonymous type symbol with the given member types member names.
///
public INamedTypeSymbol CreateAnonymousTypeSymbol(
ImmutableArray memberTypes, ImmutableArray memberNames)
{
if (memberTypes.IsDefault)
{
throw new ArgumentNullException(nameof(memberTypes));
}
if (memberNames.IsDefault)
{
throw new ArgumentNullException(nameof(memberNames));
}
if (memberTypes.Length != memberNames.Length)
{
throw new ArgumentException($"{nameof(memberTypes)} and {nameof(memberNames)} must have the same length.");
}
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}]");
}
}
return CommonCreateAnonymousTypeSymbol(memberTypes, memberNames);
}
protected abstract INamedTypeSymbol CommonCreateAnonymousTypeSymbol(
ImmutableArray memberTypes, ImmutableArray memberNames);
#endregion
#region Diagnostics
internal static readonly 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());
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)
{
bool hasError = false;
bool reportSuppressedDiagnostics = Options.ReportSuppressedDiagnostics;
foreach (Diagnostic d in incoming)
{
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 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,
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,
DiagnosticBag diagnostics,
Predicate filterOpt,
CancellationToken cancellationToken);
///
/// Update resources and generate XML documentation comments.
///
/// True if successful.
internal abstract bool GenerateResourcesAndDocumentationComments(
CommonPEModuleBuilder moduleBeingBuilt,
Stream xmlDocumentationStream,
Stream win32ResourcesStream,
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,
diagnostics,
filterOpt,
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,
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);
}
}
///
/// Emit the IL for the compiled source code into the specified stream.
///
/// Stream to which the compilation 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.
/// To cancel the emit process.
[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,
null,
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 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).
///
/// 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,
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));
}
}
return Emit(
peStream,
pdbStream,
xmlDocumentationStream,
win32Resources,
manifestResources,
options,
debugEntryPoint,
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 pdbStream,
Stream xmlDocumentationStream,
Stream win32Resources,
IEnumerable manifestResources,
EmitOptions options,
IMethodSymbol debugEntryPoint,
CompilationTestData testData,
CancellationToken cancellationToken)
{
bool embedPdb = options?.DebugInformationFormat == DebugInformationFormat.Embedded;
Debug.Assert(!embedPdb || pdbStream == null);
var diagnostics = DiagnosticBag.GetInstance();
var moduleBeingBuilt = CheckOptionsAndCreateModuleBuilder(
diagnostics,
manifestResources,
options,
debugEntryPoint,
testData,
cancellationToken);
bool success = false;
if (moduleBeingBuilt != null)
{
try
{
success = CompileMethods(
moduleBeingBuilt,
emittingPdb: pdbStream != null || embedPdb,
diagnostics: diagnostics,
filterOpt: null,
cancellationToken: cancellationToken);
if (!moduleBeingBuilt.EmitOptions.EmitMetadataOnly)
{
if (!GenerateResourcesAndDocumentationComments(
moduleBeingBuilt,
xmlDocumentationStream,
win32Resources,
diagnostics,
cancellationToken))
{
success = false;
}
if (success)
{
ReportUnusedImports(null, diagnostics, cancellationToken);
}
}
}
finally
{
moduleBeingBuilt.CompilationFinished();
}
if (success)
{
success = SerializeToPeStream(
moduleBeingBuilt,
new SimpleEmitStreamProvider(peStream),
(pdbStream != null) ? new SimpleEmitStreamProvider(pdbStream) : null,
testData?.SymWriterFactory,
diagnostics,
metadataOnly: (options != null) && options.EmitMetadataOnly,
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,
CompilationTestData testData,
CancellationToken cancellationToken)
{
if (options != null)
{
options.ValidateOptions(diagnostics, this.MessageProvider);
}
else
{
options = EmitOptions.Default;
}
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,
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 pdbStreamProvider,
Func