// 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.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Instrumentation;
using Microsoft.CodeAnalysis.Symbols;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
///
/// 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 sealed partial class CSharpCompilation : Compilation
{
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// Changes to the public interface of this class should remain synchronized with the VB
// version. Do not make any changes to the public interface without making the corresponding
// change to the VB version.
//
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
internal static readonly ParallelOptions DefaultParallelOptions = new ParallelOptions();
private readonly CSharpCompilationOptions options;
private readonly ImmutableArray syntaxTrees; // In ordinal order.
private readonly ImmutableDictionary> rootNamespaces;
private readonly DeclarationTable declarationTable;
private readonly Lazy globalImports;
private readonly Lazy globalNamespaceAlias; // alias symbol used to resolve "global::".
private readonly Lazy scriptClass;
private readonly CSharpCompilation previousSubmission;
// All imports (using directives and extern aliases) in syntax trees in this compilation.
// NOTE: We need to de-dup since the Imports objects that populate the list may be GC'd
// and re-created.
private ConcurrentSet lazyImportInfos;
// Cache the CLS diagnostics for the whole compilation so they aren't computed repeatedly.
// NOTE: Presently, we do not cache the per-tree diagnostics.
private ImmutableArray lazyClsComplianceDiagnostics;
private Conversions conversions;
internal Conversions Conversions
{
get
{
if (conversions == null)
{
Interlocked.CompareExchange(ref conversions, new BuckStopsHereBinder(this).Conversions, null);
}
return conversions;
}
}
///
/// Manages anonymous types declared in this compilation. Unifies types that are structurally equivalent.
///
private AnonymousTypeManager anonymousTypeManager;
private NamespaceSymbol lazyGlobalNamespace;
internal readonly BuiltInOperators builtInOperators;
///
/// The for this compilation. Do not access directly, use Assembly property
/// instead. This field is lazily initialized by ReferenceManager, ReferenceManager.CacheLockObject must be locked
/// while ReferenceManager "calculates" the value and assigns it, several threads must not perform duplicate
/// "calculation" simultaneously.
///
private SourceAssemblySymbol lazyAssemblySymbol;
///
/// Holds onto data related to reference binding.
/// The manager is shared among multiple compilations that we expect to have the same result of reference binding.
/// In most cases this can be determined without performing the binding. If the compilation however contains a circular
/// metadata reference (a metadata reference that refers back to the compilation) we need to avoid sharing of the binding results.
/// We do so by creating a new reference manager for such compilation.
///
private ReferenceManager referenceManager;
///
/// Contains the main method of this assembly, if there is one.
///
private EntryPoint lazyEntryPoint;
///
/// The set of trees for which a has been added to the queue.
///
private HashSet lazyCompilationUnitCompletedTrees;
public override string Language
{
get
{
return LanguageNames.CSharp;
}
}
public override bool IsCaseSensitive
{
get
{
return true;
}
}
///
/// The options the compilation was created with.
///
public new CSharpCompilationOptions Options
{
get
{
return options;
}
}
internal AnonymousTypeManager AnonymousTypeManager
{
get
{
return anonymousTypeManager;
}
}
internal override CommonAnonymousTypeManager CommonAnonymousTypeManager
{
get
{
return AnonymousTypeManager;
}
}
///
/// The language version that was used to parse the syntax trees of this compilation.
///
public LanguageVersion LanguageVersion
{
get; private set;
}
public override INamedTypeSymbol CreateErrorTypeSymbol(INamespaceOrTypeSymbol container, string name, int arity)
{
return new ExtendedErrorTypeSymbol((NamespaceOrTypeSymbol)container, name, arity, null);
}
#region Constructors and Factories
private static CSharpCompilationOptions DefaultOptions = new CSharpCompilationOptions(OutputKind.ConsoleApplication);
private static CSharpCompilationOptions DefaultSubmissionOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
///
/// Creates a new compilation from scratch. Methods such as AddSyntaxTrees or AddReferences
/// on the returned object will allow to continue building up the Compilation incrementally.
///
/// Simple assembly name.
/// The syntax trees with the source code for the new compilation.
/// The references for the new compilation.
/// The compiler options to use.
/// A new compilation.
public static CSharpCompilation Create(
string assemblyName,
IEnumerable syntaxTrees = null,
IEnumerable references = null,
CSharpCompilationOptions options = null)
{
return Create(
assemblyName,
options ?? DefaultOptions,
(syntaxTrees != null) ? syntaxTrees.Cast() : null,
references,
previousSubmission: null,
returnType: null,
hostObjectType: null,
isSubmission: false);
}
///
/// Creates a new compilation that can be used in scripting.
///
public static CSharpCompilation CreateSubmission(
string assemblyName,
SyntaxTree syntaxTree = null,
IEnumerable references = null,
CSharpCompilationOptions options = null,
Compilation previousSubmission = null,
Type returnType = null,
Type hostObjectType = null)
{
CheckSubmissionOptions(options);
return Create(
assemblyName,
options ?? DefaultSubmissionOptions,
(syntaxTree != null) ? new[] { syntaxTree } : SpecializedCollections.EmptyEnumerable(),
references,
(CSharpCompilation)previousSubmission,
returnType,
hostObjectType,
isSubmission: true);
}
private static CSharpCompilation Create(
string assemblyName,
CSharpCompilationOptions options,
IEnumerable syntaxTrees,
IEnumerable references,
CSharpCompilation previousSubmission,
Type returnType,
Type hostObjectType,
bool isSubmission)
{
Debug.Assert(options != null);
CheckAssemblyName(assemblyName);
var validatedReferences = ValidateReferences(references);
ValidateSubmissionParameters(previousSubmission, returnType, ref hostObjectType);
var compilation = new CSharpCompilation(
assemblyName,
options,
validatedReferences,
ImmutableArray.Empty,
ImmutableDictionary.Create(ReferenceEqualityComparer.Instance),
ImmutableDictionary.Create>(),
DeclarationTable.Empty,
previousSubmission,
returnType,
hostObjectType,
isSubmission,
referenceManager: null,
reuseReferenceManager: false);
if (syntaxTrees != null)
{
compilation = compilation.AddSyntaxTrees(syntaxTrees);
}
Debug.Assert((object)compilation.lazyAssemblySymbol == null);
return compilation;
}
private CSharpCompilation(
string assemblyName,
CSharpCompilationOptions options,
ImmutableArray references,
ImmutableArray syntaxTrees,
ImmutableDictionary syntaxTreeOrdinalMap,
ImmutableDictionary> rootNamespaces,
DeclarationTable declarationTable,
CSharpCompilation previousSubmission,
Type submissionReturnType,
Type hostObjectType,
bool isSubmission,
ReferenceManager referenceManager,
bool reuseReferenceManager,
AsyncQueue eventQueue = null)
: base(assemblyName, references, submissionReturnType, hostObjectType, isSubmission, syntaxTreeOrdinalMap, eventQueue)
{
using (Logger.LogBlock(FunctionId.CSharp_Compilation_Create, message: assemblyName))
{
this.wellKnownMemberSignatureComparer = new WellKnownMembersSignatureComparer(this);
this.options = options;
this.syntaxTrees = syntaxTrees;
this.rootNamespaces = rootNamespaces;
this.declarationTable = declarationTable;
Debug.Assert(syntaxTrees.All(tree => syntaxTrees[syntaxTreeOrdinalMap[tree]] == tree));
Debug.Assert(syntaxTrees.SetEquals(rootNamespaces.Keys.AsImmutable(), EqualityComparer.Default));
this.builtInOperators = new BuiltInOperators(this);
this.scriptClass = new Lazy(BindScriptClass);
this.globalImports = new Lazy(BindGlobalUsings);
this.globalNamespaceAlias = new Lazy(CreateGlobalNamespaceAlias);
this.anonymousTypeManager = new AnonymousTypeManager(this);
this.LanguageVersion = CommonLanguageVersion(syntaxTrees);
if (isSubmission)
{
Debug.Assert(previousSubmission == null || previousSubmission.HostObjectType == hostObjectType);
this.previousSubmission = previousSubmission;
}
else
{
Debug.Assert(previousSubmission == null && submissionReturnType == null && hostObjectType == null);
}
if (reuseReferenceManager)
{
referenceManager.AssertCanReuseForCompilation(this);
this.referenceManager = referenceManager;
}
else
{
this.referenceManager = new ReferenceManager(
MakeSourceAssemblySimpleName(),
options.AssemblyIdentityComparer,
(referenceManager != null) ? referenceManager.ObservedMetadata : null);
}
Debug.Assert((object)this.lazyAssemblySymbol == null);
if (EventQueue != null) EventQueue.Enqueue(new CompilationStartedEvent(this));
}
}
private static LanguageVersion CommonLanguageVersion(ImmutableArray syntaxTrees)
{
LanguageVersion? result = null;
foreach (var tree in syntaxTrees)
{
var version = ((CSharpParseOptions)tree.Options).LanguageVersion;
if (result == null)
{
result = version;
}
else if (result != version)
{
throw new ArgumentException("inconsistent language versions", nameof(syntaxTrees));
}
}
return result ?? CSharpParseOptions.Default.LanguageVersion;
}
///
/// Create a duplicate of this compilation with different symbol instances.
///
public new CSharpCompilation Clone()
{
return new CSharpCompilation(
this.AssemblyName,
this.options,
this.ExternalReferences,
this.SyntaxTrees,
this.syntaxTreeOrdinalMap,
this.rootNamespaces,
this.declarationTable,
this.previousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
this.referenceManager,
reuseReferenceManager: true);
}
private CSharpCompilation UpdateSyntaxTrees(
ImmutableArray syntaxTrees,
ImmutableDictionary syntaxTreeOrdinalMap,
ImmutableDictionary> rootNamespaces,
DeclarationTable declarationTable,
bool referenceDirectivesChanged)
{
return new CSharpCompilation(
this.AssemblyName,
this.options,
this.ExternalReferences,
syntaxTrees,
syntaxTreeOrdinalMap,
rootNamespaces,
declarationTable,
this.previousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
this.referenceManager,
reuseReferenceManager: !referenceDirectivesChanged);
}
///
/// Creates a new compilation with the specified name.
///
public new CSharpCompilation WithAssemblyName(string assemblyName)
{
CheckAssemblyName(assemblyName);
// Can't reuse references since the source assembly name changed and the referenced symbols might
// have internals-visible-to relationship with this compilation or they might had a circular reference
// to this compilation.
return new CSharpCompilation(
assemblyName,
this.options,
this.ExternalReferences,
this.SyntaxTrees,
this.syntaxTreeOrdinalMap,
this.rootNamespaces,
this.declarationTable,
this.previousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
this.referenceManager,
reuseReferenceManager: assemblyName == this.AssemblyName);
}
///
/// Creates a new compilation with the specified references.
///
///
/// The new will query the given for the underlying
/// metadata as soon as the are needed.
///
/// The new compilation uses whatever metadata is currently being provided by the .
/// E.g. if the current compilation references a metadata file that has changed since the creation of the compilation
/// the new compilation is going to use the updated version, while the current compilation will be using the previous (it doesn't change).
///
public new CSharpCompilation WithReferences(IEnumerable references)
{
// References might have changed, don't reuse reference manager.
// Don't even reuse observed metadata - let the manager query for the metadata again.
return new CSharpCompilation(
this.AssemblyName,
this.options,
ValidateReferences(references),
this.SyntaxTrees,
this.syntaxTreeOrdinalMap,
this.rootNamespaces,
this.declarationTable,
this.previousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
referenceManager: null,
reuseReferenceManager: false);
}
///
/// Creates a new compilation with the specified references.
///
public new CSharpCompilation WithReferences(params MetadataReference[] references)
{
return this.WithReferences((IEnumerable)references);
}
///
/// Creates a new compilation with the specified compilation options.
///
public CSharpCompilation WithOptions(CSharpCompilationOptions options)
{
// Checks to see if the new options support reusing the reference manager
bool reuseReferenceManager = this.Options.CanReuseCompilationReferenceManager(options);
return new CSharpCompilation(
this.AssemblyName,
options,
this.ExternalReferences,
this.syntaxTrees,
this.syntaxTreeOrdinalMap,
this.rootNamespaces,
this.declarationTable,
this.previousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
this.referenceManager,
reuseReferenceManager);
}
///
/// Returns a new compilation with the given compilation set as the previous submission.
///
internal CSharpCompilation WithPreviousSubmission(CSharpCompilation newPreviousSubmission)
{
if (!this.IsSubmission)
{
throw new NotSupportedException("Can't have a previousSubmission when not a submission");
}
// Reference binding doesn't depend on previous submission so we can reuse it.
return new CSharpCompilation(
this.AssemblyName,
this.options,
this.ExternalReferences,
this.SyntaxTrees,
this.syntaxTreeOrdinalMap,
this.rootNamespaces,
this.declarationTable,
newPreviousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
this.referenceManager,
reuseReferenceManager: true);
}
///
/// Returns a new compilation with a given event queue.
///
internal override Compilation WithEventQueue(AsyncQueue eventQueue)
{
return new CSharpCompilation(
this.AssemblyName,
this.options,
this.ExternalReferences,
this.SyntaxTrees,
this.syntaxTreeOrdinalMap,
this.rootNamespaces,
this.declarationTable,
this.previousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
this.referenceManager,
reuseReferenceManager: true,
eventQueue: eventQueue);
}
#endregion
#region Submission
internal new CSharpCompilation PreviousSubmission
{
get { return previousSubmission; }
}
// TODO (tomat): consider moving this method to SemanticModel
///
/// Returns the type of the submission return value.
///
///
/// The type of the last expression of the submission.
/// Null if the type of the last expression is unknown (null).
/// Void type if the type of the last expression statement is void or
/// the submission ends with a declaration or statement that is not an expression statement.
///
///
/// Note that the return type is System.Void for both compilations "System.Console.WriteLine();" and "System.Console.WriteLine()",
/// and is False for the former and True for the latter.
///
/// True if the submission has value, i.e. if it ends with a statement that is an expression statement.
/// The compilation doesn't represent a submission ( return false).
internal new TypeSymbol GetSubmissionResultType(out bool hasValue)
{
if (!IsSubmission)
{
throw new InvalidOperationException(CSharpResources.ThisCompilationNotInteractive);
}
hasValue = false;
// submission can be empty or comprise of a script file
SyntaxTree tree = SyntaxTrees.SingleOrDefault();
if (tree == null || tree.Options.Kind != SourceCodeKind.Interactive)
{
return GetSpecialType(SpecialType.System_Void);
}
var lastStatement = (GlobalStatementSyntax)tree.GetCompilationUnitRoot().Members.LastOrDefault(decl => decl.Kind() == SyntaxKind.GlobalStatement);
if (lastStatement == null || lastStatement.Statement.Kind() != SyntaxKind.ExpressionStatement)
{
return GetSpecialType(SpecialType.System_Void);
}
var expressionStatement = (ExpressionStatementSyntax)lastStatement.Statement;
if (!expressionStatement.SemicolonToken.IsMissing)
{
return GetSpecialType(SpecialType.System_Void);
}
var model = GetSemanticModel(tree);
hasValue = true;
var expression = expressionStatement.Expression;
var info = model.GetTypeInfo(expression);
return (TypeSymbol)info.ConvertedType;
}
#endregion
#region Syntax Trees (maintain an ordered list)
///
/// The syntax trees (parsed from source code) that this compilation was created with.
///
public new ImmutableArray SyntaxTrees
{
get { return this.syntaxTrees; }
}
///
/// Returns true if this compilation contains the specified tree. False otherwise.
///
public new bool ContainsSyntaxTree(SyntaxTree syntaxTree)
{
var cstree = syntaxTree as SyntaxTree;
return cstree != null && rootNamespaces.ContainsKey((cstree));
}
///
/// Creates a new compilation with additional syntax trees.
///
public new CSharpCompilation AddSyntaxTrees(params SyntaxTree[] trees)
{
return AddSyntaxTrees((IEnumerable)trees);
}
///
/// Creates a new compilation with additional syntax trees.
///
public new CSharpCompilation AddSyntaxTrees(IEnumerable trees)
{
using (Logger.LogBlock(FunctionId.CSharp_Compilation_AddSyntaxTrees, message: this.AssemblyName))
{
if (trees == null)
{
throw new ArgumentNullException("trees");
}
if (trees.IsEmpty())
{
return this;
}
// We're using a try-finally for this builder because there's a test that
// specifically checks for one or more of the argument exceptions below
// and we don't want to see console spew (even though we don't generally
// care about pool "leaks" in exceptional cases). Alternatively, we
// could create a new ArrayBuilder.
var builder = ArrayBuilder.GetInstance();
try
{
builder.AddRange(this.SyntaxTrees);
bool referenceDirectivesChanged = false;
var oldTreeCount = this.SyntaxTrees.Length;
var ordinalMap = this.syntaxTreeOrdinalMap;
var declMap = rootNamespaces;
var declTable = declarationTable;
int i = 0;
foreach (var tree in trees.Cast())
{
if (tree == null)
{
throw new ArgumentNullException("trees[" + i + "]");
}
if (!tree.HasCompilationUnitRoot)
{
throw new ArgumentException(String.Format(CSharpResources.TreeMustHaveARootNodeWith, i));
}
if (declMap.ContainsKey(tree))
{
throw new ArgumentException(CSharpResources.SyntaxTreeAlreadyPresent, String.Format(CSharpResources.Trees0, i));
}
if (IsSubmission && tree.Options.Kind == SourceCodeKind.Regular)
{
throw new ArgumentException(CSharpResources.SubmissionCanOnlyInclude, String.Format(CSharpResources.Trees0, i));
}
AddSyntaxTreeToDeclarationMapAndTable(tree, options, IsSubmission, ref declMap, ref declTable, ref referenceDirectivesChanged);
builder.Add(tree);
ordinalMap = ordinalMap.Add(tree, oldTreeCount + i);
i++;
}
if (IsSubmission && declMap.Count > 1)
{
throw new ArgumentException(CSharpResources.SubmissionCanHaveAtMostOne, "trees");
}
return UpdateSyntaxTrees(builder.ToImmutable(), ordinalMap, declMap, declTable, referenceDirectivesChanged);
}
finally
{
builder.Free();
}
}
}
private static void AddSyntaxTreeToDeclarationMapAndTable(
SyntaxTree tree,
CSharpCompilationOptions options,
bool isSubmission,
ref ImmutableDictionary> declMap,
ref DeclarationTable declTable,
ref bool referenceDirectivesChanged)
{
var lazyRoot = new Lazy(() => DeclarationTreeBuilder.ForTree(tree, options.ScriptClassName ?? "", isSubmission));
declMap = declMap.SetItem(tree, lazyRoot);
declTable = declTable.AddRootDeclaration(lazyRoot);
referenceDirectivesChanged = referenceDirectivesChanged || tree.HasReferenceDirectives();
}
///
/// Creates a new compilation without the specified syntax trees. Preserves metadata info for use with trees
/// added later.
///
public new CSharpCompilation RemoveSyntaxTrees(params SyntaxTree[] trees)
{
return RemoveSyntaxTrees((IEnumerable)trees);
}
///
/// Creates a new compilation without the specified syntax trees. Preserves metadata info for use with trees
/// added later.
///
public new CSharpCompilation RemoveSyntaxTrees(IEnumerable trees)
{
using (Logger.LogBlock(FunctionId.CSharp_Compilation_RemoveSyntaxTrees, message: this.AssemblyName))
{
if (trees == null)
{
throw new ArgumentNullException("trees");
}
if (trees.IsEmpty())
{
return this;
}
bool referenceDirectivesChanged = false;
var removeSet = new HashSet();
var declMap = rootNamespaces;
var declTable = declarationTable;
foreach (var tree in trees.Cast())
{
RemoveSyntaxTreeFromDeclarationMapAndTable(tree, ref declMap, ref declTable, ref referenceDirectivesChanged);
removeSet.Add(tree);
}
Debug.Assert(!removeSet.IsEmpty());
// We're going to have to revise the ordinals of all
// trees after the first one removed, so just build
// a new map.
var ordinalMap = ImmutableDictionary.Create();
var builder = ArrayBuilder.GetInstance();
int i = 0;
foreach (var tree in this.SyntaxTrees)
{
if (!removeSet.Contains(tree))
{
builder.Add(tree);
ordinalMap = ordinalMap.Add(tree, i++);
}
}
return UpdateSyntaxTrees(builder.ToImmutableAndFree(), ordinalMap, declMap, declTable, referenceDirectivesChanged);
}
}
private static void RemoveSyntaxTreeFromDeclarationMapAndTable(
SyntaxTree tree,
ref ImmutableDictionary> declMap,
ref DeclarationTable declTable,
ref bool referenceDirectivesChanged)
{
Lazy lazyRoot;
if (!declMap.TryGetValue(tree, out lazyRoot))
{
throw new ArgumentException(string.Format(CSharpResources.SyntaxTreeNotFoundTo, tree), "trees");
}
declTable = declTable.RemoveRootDeclaration(lazyRoot);
declMap = declMap.Remove(tree);
referenceDirectivesChanged = referenceDirectivesChanged || tree.HasReferenceDirectives();
}
///
/// Creates a new compilation without any syntax trees. Preserves metadata info
/// from this compilation for use with trees added later.
///
public new CSharpCompilation RemoveAllSyntaxTrees()
{
return UpdateSyntaxTrees(
ImmutableArray.Empty,
ImmutableDictionary.Create(),
ImmutableDictionary.Create>(),
DeclarationTable.Empty,
referenceDirectivesChanged: declarationTable.ReferenceDirectives.Any());
}
///
/// Creates a new compilation without the old tree but with the new tree.
///
public new CSharpCompilation ReplaceSyntaxTree(SyntaxTree oldTree, SyntaxTree newTree)
{
using (Logger.LogBlock(FunctionId.CSharp_Compilation_ReplaceSyntaxTree, message: this.AssemblyName))
{
// this is just to force a cast exception
oldTree = (CSharpSyntaxTree)oldTree;
newTree = (CSharpSyntaxTree)newTree;
if (oldTree == null)
{
throw new ArgumentNullException("oldTree");
}
if (newTree == null)
{
return this.RemoveSyntaxTrees(oldTree);
}
else if (newTree == oldTree)
{
return this;
}
if (!newTree.HasCompilationUnitRoot)
{
throw new ArgumentException(CSharpResources.TreeMustHaveARootNodeWith, "newTree");
}
var declMap = rootNamespaces;
var declTable = declarationTable;
bool referenceDirectivesChanged = false;
// TODO(tomat): Consider comparing #r's of the old and the new tree. If they are exactly the same we could still reuse.
// This could be a perf win when editing a script file in the IDE. The services create a new compilation every keystroke
// that replaces the tree with a new one.
RemoveSyntaxTreeFromDeclarationMapAndTable(oldTree, ref declMap, ref declTable, ref referenceDirectivesChanged);
AddSyntaxTreeToDeclarationMapAndTable(newTree, options, this.IsSubmission, ref declMap, ref declTable, ref referenceDirectivesChanged);
var ordinalMap = this.syntaxTreeOrdinalMap;
Debug.Assert(ordinalMap.ContainsKey(oldTree)); // Checked by RemoveSyntaxTreeFromDeclarationMapAndTable
var oldOrdinal = ordinalMap[oldTree];
var newArray = this.SyntaxTrees.SetItem(oldOrdinal, newTree);
// CONSIDER: should this be an operation on ImmutableDictionary?
ordinalMap = ordinalMap.Remove(oldTree);
ordinalMap = ordinalMap.SetItem(newTree, oldOrdinal);
return UpdateSyntaxTrees(newArray, ordinalMap, declMap, declTable, referenceDirectivesChanged);
}
}
#endregion
#region References
internal override CommonReferenceManager CommonGetBoundReferenceManager()
{
return GetBoundReferenceManager();
}
internal new ReferenceManager GetBoundReferenceManager()
{
if ((object)lazyAssemblySymbol == null)
{
referenceManager.CreateSourceAssemblyForCompilation(this);
Debug.Assert((object)lazyAssemblySymbol != null);
}
// referenceManager can only be accessed after we initialized the lazyAssemblySymbol.
// In fact, initialization of the assembly symbol might change the reference manager.
return referenceManager;
}
// for testing only:
internal bool ReferenceManagerEquals(CSharpCompilation other)
{
return ReferenceEquals(this.referenceManager, other.referenceManager);
}
public override ImmutableArray DirectiveReferences
{
get
{
return GetBoundReferenceManager().DirectiveReferences;
}
}
internal override IDictionary ReferenceDirectiveMap
{
get
{
return GetBoundReferenceManager().ReferenceDirectiveMap;
}
}
// for testing purposes
internal IEnumerable ExternAliases
{
get
{
return GetBoundReferenceManager().ExternAliases;
}
}
///
/// Gets the or for a metadata reference used to create this compilation.
///
/// or corresponding to the given reference or null if there is none.
///
/// Uses object identity when comparing two references.
///
internal new Symbol GetAssemblyOrModuleSymbol(MetadataReference reference)
{
if (reference == null)
{
throw new ArgumentNullException("reference");
}
if (reference.Properties.Kind == MetadataImageKind.Assembly)
{
return GetBoundReferenceManager().GetReferencedAssemblySymbol(reference);
}
else
{
Debug.Assert(reference.Properties.Kind == MetadataImageKind.Module);
int index = GetBoundReferenceManager().GetReferencedModuleIndex(reference);
return index < 0 ? null : this.Assembly.Modules[index];
}
}
public override IEnumerable ReferencedAssemblyNames
{
get
{
return Assembly.Modules.SelectMany(module => module.GetReferencedAssemblies());
}
}
///
/// All reference directives used in this compilation.
///
internal override IEnumerable ReferenceDirectives
{
get { return declarationTable.ReferenceDirectives; }
}
///
/// Returns a metadata reference that a given #r resolves to.
///
/// #r directive.
/// Metadata reference the specified directive resolves to.
public MetadataReference GetDirectiveReference(ReferenceDirectiveTriviaSyntax directive)
{
return ReferenceDirectiveMap[directive.File.ValueText];
}
///
/// Creates a new compilation with additional metadata references.
///
public new CSharpCompilation AddReferences(params MetadataReference[] references)
{
return (CSharpCompilation)base.AddReferences(references);
}
///
/// Creates a new compilation with additional metadata references.
///
public new CSharpCompilation AddReferences(IEnumerable references)
{
return (CSharpCompilation)base.AddReferences(references);
}
///
/// Creates a new compilation without the specified metadata references.
///
public new CSharpCompilation RemoveReferences(params MetadataReference[] references)
{
return (CSharpCompilation)base.RemoveReferences(references);
}
///
/// Creates a new compilation without the specified metadata references.
///
public new CSharpCompilation RemoveReferences(IEnumerable references)
{
return (CSharpCompilation)base.RemoveReferences(references);
}
///
/// Creates a new compilation without any metadata references
///
public new CSharpCompilation RemoveAllReferences()
{
return (CSharpCompilation)base.RemoveAllReferences();
}
///
/// Creates a new compilation with an old metadata reference replaced with a new metadata reference.
///
public new CSharpCompilation ReplaceReference(MetadataReference oldReference, MetadataReference newReference)
{
return (CSharpCompilation)base.ReplaceReference(oldReference, newReference);
}
public override CompilationReference ToMetadataReference(ImmutableArray aliases = default(ImmutableArray), bool embedInteropTypes = false)
{
return new CSharpCompilationReference(this, aliases, embedInteropTypes);
}
// Get all modules in this compilation, including the source module, added modules, and all
// modules of referenced assemblies that do not come from an assembly with an extern alias.
// Metadata imported from aliased assemblies is not visible at the source level except through
// the use of an extern alias directive. So exclude them from this list which is used to construct
// the global namespace.
private IEnumerable GetAllUnaliasedModules()
{
// Get all assemblies in this compilation, including the source assembly and all referenced assemblies.
ArrayBuilder modules = new ArrayBuilder();
// NOTE: This includes referenced modules - they count as modules of the compilation assembly.
modules.AddRange(this.Assembly.Modules);
foreach (var pair in GetBoundReferenceManager().ReferencedAssembliesMap)
{
MetadataReference reference = pair.Key;
ReferenceManager.ReferencedAssembly referencedAssembly = pair.Value;
if (reference.Properties.Kind == MetadataImageKind.Assembly) // Already handled modules above.
{
if (referencedAssembly.DeclarationsAccessibleWithoutAlias())
{
modules.AddRange(referencedAssembly.Symbol.Modules);
}
}
}
return modules;
}
///
/// Gets the that corresponds to the assembly symbol.
///
public new MetadataReference GetMetadataReference(IAssemblySymbol assemblySymbol)
{
return this.GetBoundReferenceManager().ReferencedAssembliesMap.Where(kvp => object.ReferenceEquals(kvp.Value.Symbol, assemblySymbol)).Select(kvp => kvp.Key).FirstOrDefault();
}
#endregion
#region Symbols
///
/// The AssemblySymbol that represents the assembly being created.
///
internal SourceAssemblySymbol SourceAssembly
{
get
{
GetBoundReferenceManager();
return lazyAssemblySymbol;
}
}
///
/// The AssemblySymbol that represents the assembly being created.
///
internal new AssemblySymbol Assembly
{
get
{
return SourceAssembly;
}
}
///
/// Get a ModuleSymbol that refers to the module being created by compiling all of the code.
/// By getting the GlobalNamespace property of that module, all of the namespaces and types
/// defined in source code can be obtained.
///
internal new ModuleSymbol SourceModule
{
get
{
return Assembly.Modules[0];
}
}
///
/// Gets the root namespace that contains all namespaces and types defined in source code or in
/// referenced metadata, merged into a single namespace hierarchy.
///
internal new NamespaceSymbol GlobalNamespace
{
get
{
if ((object)lazyGlobalNamespace == null)
{
using (Logger.LogBlock(FunctionId.CSharp_Compilation_GetGlobalNamespace, message: this.AssemblyName))
{
// Get the root namespace from each module, and merge them all together
HashSet allGlobalNamespaces = new HashSet();
foreach (ModuleSymbol module in GetAllUnaliasedModules())
{
allGlobalNamespaces.Add(module.GlobalNamespace);
}
var result = MergedNamespaceSymbol.Create(new NamespaceExtent(this),
null,
allGlobalNamespaces.AsImmutable());
Interlocked.CompareExchange(ref lazyGlobalNamespace, result, null);
}
}
return lazyGlobalNamespace;
}
}
///
/// Given for the specified module or assembly namespace, gets the corresponding compilation
/// namespace (merged namespace representation for all namespace declarations and references
/// with contributions for the namespaceSymbol). Can return null if no corresponding
/// namespace can be bound in this compilation with the same name.
///
internal new NamespaceSymbol GetCompilationNamespace(INamespaceSymbol namespaceSymbol)
{
if (namespaceSymbol is NamespaceSymbol &&
namespaceSymbol.NamespaceKind == NamespaceKind.Compilation &&
namespaceSymbol.ContainingCompilation == this)
{
return (NamespaceSymbol)namespaceSymbol;
}
var containingNamespace = namespaceSymbol.ContainingNamespace;
if (containingNamespace == null)
{
return this.GlobalNamespace;
}
var current = GetCompilationNamespace(containingNamespace);
if ((object)current != null)
{
return current.GetNestedNamespace(namespaceSymbol.Name);
}
return null;
}
private ConcurrentDictionary externAliasTargets;
internal bool GetExternAliasTarget(string aliasName, out NamespaceSymbol @namespace)
{
if (externAliasTargets == null)
{
Interlocked.CompareExchange(ref this.externAliasTargets, new ConcurrentDictionary(), null);
}
else if (externAliasTargets.TryGetValue(aliasName, out @namespace))
{
return !(@namespace is MissingNamespaceSymbol);
}
ArrayBuilder builder = null;
foreach (var referencedAssembly in GetBoundReferenceManager().ReferencedAssembliesMap.Values)
{
if (referencedAssembly.Aliases.Contains(aliasName))
{
builder = builder ?? ArrayBuilder.GetInstance();
builder.Add(referencedAssembly.Symbol.GlobalNamespace);
}
}
bool foundNamespace = builder != null;
// We want to cache failures as well as successes so that subsequent incorrect extern aliases with the
// same alias will have the same target.
@namespace = foundNamespace
? MergedNamespaceSymbol.Create(new NamespaceExtent(this), namespacesToMerge: builder.ToImmutableAndFree(), containingNamespace: null, nameOpt: null)
: new MissingNamespaceSymbol(new MissingModuleSymbol(new MissingAssemblySymbol(new AssemblyIdentity(System.Guid.NewGuid().ToString())), ordinal: -1));
// Use GetOrAdd in case another thread beat us to the punch (i.e. should return the same object for the same alias, every time).
@namespace = externAliasTargets.GetOrAdd(aliasName, @namespace);
Debug.Assert(foundNamespace == !(@namespace is MissingNamespaceSymbol));
return foundNamespace;
}
///
/// A symbol representing the implicit Script class. This is null if the class is not
/// defined in the compilation.
///
internal new NamedTypeSymbol ScriptClass
{
get { return scriptClass.Value; }
}
///
/// 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.
private ImplicitNamedTypeSymbol BindScriptClass()
{
if (options.ScriptClassName == null || !options.ScriptClassName.IsValidClrTypeName())
{
return null;
}
var namespaceOrType = this.Assembly.GlobalNamespace.GetNamespaceOrTypeByQualifiedName(options.ScriptClassName.Split('.')).AsSingleton();
return namespaceOrType as ImplicitNamedTypeSymbol;
}
internal Imports GlobalImports
{
get { return globalImports.Value; }
}
internal IEnumerable GlobalUsings
{
get
{
return GlobalImports.Usings.Select(u => u.NamespaceOrType);
}
}
internal AliasSymbol GlobalNamespaceAlias
{
get
{
return globalNamespaceAlias.Value;
}
}
///
/// Get the symbol for the predefined type from the COR Library referenced by this compilation.
///
internal new NamedTypeSymbol GetSpecialType(SpecialType specialType)
{
if (specialType <= SpecialType.None || specialType > SpecialType.Count)
{
throw new ArgumentOutOfRangeException("specialType");
}
var result = Assembly.GetSpecialType(specialType);
Debug.Assert(result.SpecialType == specialType);
return result;
}
///
/// Get the symbol for the predefined type member from the COR Library referenced by this compilation.
///
internal Symbol GetSpecialTypeMember(SpecialMember specialMember)
{
return Assembly.GetSpecialTypeMember(specialMember);
}
internal TypeSymbol GetTypeByReflectionType(Type type, DiagnosticBag diagnostics)
{
var result = Assembly.GetTypeByReflectionType(type, includeReferences: true);
if ((object)result == null)
{
var errorType = new ExtendedErrorTypeSymbol(this, type.Name, 0, CreateReflectionTypeNotFoundError(type));
diagnostics.Add(errorType.ErrorInfo, NoLocation.Singleton);
result = errorType;
}
return result;
}
private static CSDiagnosticInfo CreateReflectionTypeNotFoundError(Type type)
{
// The type or namespace name '{0}' could not be found in the global namespace (are you missing an assembly reference?)
return new CSDiagnosticInfo(
ErrorCode.ERR_GlobalSingleTypeNameNotFound,
new object[] { type.AssemblyQualifiedName },
ImmutableArray.Empty,
ImmutableArray.Empty
);
}
// The type of host object model if available.
private TypeSymbol lazyHostObjectTypeSymbol;
internal TypeSymbol GetHostObjectTypeSymbol()
{
if (HostObjectType != null && (object)lazyHostObjectTypeSymbol == null)
{
TypeSymbol symbol = Assembly.GetTypeByReflectionType(HostObjectType, includeReferences: true);
if ((object)symbol == null)
{
MetadataTypeName mdName = MetadataTypeName.FromNamespaceAndTypeName(HostObjectType.Namespace ?? String.Empty,
HostObjectType.Name,
useCLSCompliantNameArityEncoding: true);
symbol = new MissingMetadataTypeSymbol.TopLevelWithCustomErrorInfo(
new MissingAssemblySymbol(AssemblyIdentity.FromAssemblyDefinition(HostObjectType.GetTypeInfo().Assembly)).Modules[0],
ref mdName,
CreateReflectionTypeNotFoundError(HostObjectType),
SpecialType.None);
}
Interlocked.CompareExchange(ref lazyHostObjectTypeSymbol, symbol, null);
}
return lazyHostObjectTypeSymbol;
}
internal TypeSymbol GetSubmissionReturnType()
{
if (IsSubmission && (object)ScriptClass != null)
{
// the second parameter of Script class instance constructor is the submission return value:
return ((MethodSymbol)ScriptClass.GetMembers(WellKnownMemberNames.InstanceConstructorName)[0]).Parameters[1].Type;
}
else
{
return null;
}
}
///
/// 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.
///
internal new NamedTypeSymbol GetTypeByMetadataName(string fullyQualifiedMetadataName)
{
return this.Assembly.GetTypeByMetadataName(fullyQualifiedMetadataName, includeReferences: true, isWellKnownType: false);
}
///
/// The TypeSymbol for the type 'dynamic' in this Compilation.
///
internal new TypeSymbol DynamicType
{
get
{
return AssemblySymbol.DynamicType;
}
}
///
/// The NamedTypeSymbol for the .NET System.Object type, which could have a TypeKind of
/// Error if there was no COR Library in this Compilation.
///
internal new NamedTypeSymbol ObjectType
{
get
{
return this.Assembly.ObjectType;
}
}
internal bool DeclaresTheObjectClass
{
get
{
return SourceAssembly.DeclaresTheObjectClass;
}
}
internal new MethodSymbol GetEntryPoint(CancellationToken cancellationToken)
{
EntryPoint entryPoint = GetEntryPointAndDiagnostics(cancellationToken);
return entryPoint == null ? null : entryPoint.MethodSymbol;
}
internal EntryPoint GetEntryPointAndDiagnostics(CancellationToken cancellationToken)
{
if (!this.Options.OutputKind.IsApplication())
{
return null;
}
Debug.Assert(!this.IsSubmission);
if (this.Options.MainTypeName != null && !this.Options.MainTypeName.IsValidClrTypeName())
{
Debug.Assert(!this.Options.Errors.IsDefaultOrEmpty);
return new EntryPoint(null, ImmutableArray.Empty);
}
if (this.lazyEntryPoint == null)
{
MethodSymbol entryPoint;
ImmutableArray diagnostics;
FindEntryPoint(cancellationToken, out entryPoint, out diagnostics);
Interlocked.CompareExchange(ref this.lazyEntryPoint, new EntryPoint(entryPoint, diagnostics), null);
}
return this.lazyEntryPoint;
}
private void FindEntryPoint(CancellationToken cancellationToken, out MethodSymbol entryPoint, out ImmutableArray sealedDiagnostics)
{
using (Logger.LogBlock(FunctionId.CSharp_Compilation_FindEntryPoint, message: this.AssemblyName, cancellationToken: cancellationToken))
{
DiagnosticBag diagnostics = DiagnosticBag.GetInstance();
try
{
entryPoint = null;
ArrayBuilder entryPointCandidates;
NamedTypeSymbol mainType;
string mainTypeName = this.Options.MainTypeName;
NamespaceSymbol globalNamespace = this.SourceModule.GlobalNamespace;
if (mainTypeName != null)
{
// Global code is the entry point, ignore all other Mains.
// TODO: don't special case scripts (DevDiv #13119).
if ((object)this.ScriptClass != null)
{
// CONSIDER: we could use the symbol instead of just the name.
diagnostics.Add(ErrorCode.WRN_MainIgnored, NoLocation.Singleton, mainTypeName);
return;
}
var mainTypeOrNamespace = globalNamespace.GetNamespaceOrTypeByQualifiedName(mainTypeName.Split('.')).OfMinimalArity();
if ((object)mainTypeOrNamespace == null)
{
diagnostics.Add(ErrorCode.ERR_MainClassNotFound, NoLocation.Singleton, mainTypeName);
return;
}
mainType = mainTypeOrNamespace as NamedTypeSymbol;
if ((object)mainType == null || mainType.IsGenericType || (mainType.TypeKind != TypeKind.Class && mainType.TypeKind != TypeKind.Struct))
{
diagnostics.Add(ErrorCode.ERR_MainClassNotClass, mainTypeOrNamespace.Locations.First(), mainTypeOrNamespace);
return;
}
entryPointCandidates = ArrayBuilder.GetInstance();
EntryPointCandidateFinder.FindCandidatesInSingleType(mainType, entryPointCandidates, cancellationToken);
// NOTE: Any return after this point must free entryPointCandidates.
}
else
{
mainType = null;
entryPointCandidates = ArrayBuilder.GetInstance();
EntryPointCandidateFinder.FindCandidatesInNamespace(globalNamespace, entryPointCandidates, cancellationToken);
// NOTE: Any return after this point must free entryPointCandidates.
// global code is the entry point, ignore all other Mains:
if ((object)this.ScriptClass != null)
{
foreach (var main in entryPointCandidates)
{
diagnostics.Add(ErrorCode.WRN_MainIgnored, main.Locations.First(), main);
}
entryPointCandidates.Free();
return;
}
}
DiagnosticBag warnings = DiagnosticBag.GetInstance();
var viableEntryPoints = ArrayBuilder.GetInstance();
foreach (var candidate in entryPointCandidates)
{
if (!candidate.HasEntryPointSignature())
{
// a single error for partial methods:
warnings.Add(ErrorCode.WRN_InvalidMainSig, candidate.Locations.First(), candidate);
continue;
}
if (candidate.IsGenericMethod || candidate.ContainingType.IsGenericType)
{
// a single error for partial methods:
warnings.Add(ErrorCode.WRN_MainCantBeGeneric, candidate.Locations.First(), candidate);
continue;
}
if (candidate.IsAsync)
{
diagnostics.Add(ErrorCode.ERR_MainCantBeAsync, candidate.Locations.First(), candidate);
}
viableEntryPoints.Add(candidate);
}
if ((object)mainType == null || viableEntryPoints.Count == 0)
{
diagnostics.AddRange(warnings);
}
warnings.Free();
if (viableEntryPoints.Count == 0)
{
if ((object)mainType == null)
{
diagnostics.Add(ErrorCode.ERR_NoEntryPoint, NoLocation.Singleton);
}
else
{
diagnostics.Add(ErrorCode.ERR_NoMainInClass, mainType.Locations.First(), mainType);
}
}
else if (viableEntryPoints.Count > 1)
{
viableEntryPoints.Sort(LexicalOrderSymbolComparer.Instance);
var info = new CSDiagnosticInfo(
ErrorCode.ERR_MultipleEntryPoints,
args: SpecializedCollections.EmptyArray