diff --git a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj
index 83c4dc1ec827a96a407e1e767b689def128472f1..0e0537699282b5d3e9c94849f65bbe5fa30af940 100644
--- a/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj
+++ b/src/Compilers/CSharp/Portable/CSharpCodeAnalysis.csproj
@@ -230,6 +230,8 @@
+
+
diff --git a/src/Compilers/CSharp/Portable/CSharpExtensions.cs b/src/Compilers/CSharp/Portable/CSharpExtensions.cs
index 50d543bd1a152b8f303c2d02239333db01d6f256..51b40cf8b7ddd8ceb390e43b0a6facb5630b488b 100644
--- a/src/Compilers/CSharp/Portable/CSharpExtensions.cs
+++ b/src/Compilers/CSharp/Portable/CSharpExtensions.cs
@@ -351,6 +351,12 @@ internal static bool HasReferenceDirectives(this SyntaxTree tree)
return csharpTree != null && csharpTree.HasReferenceDirectives;
}
+ internal static bool HasReferenceOrLoadDirectives(this SyntaxTree tree)
+ {
+ var csharpTree = tree as CSharpSyntaxTree;
+ return csharpTree != null && csharpTree.HasReferenceOrLoadDirectives;
+ }
+
internal static bool IsAnyPreprocessorSymbolDefined(this SyntaxTree tree, ImmutableArray conditionalSymbols)
{
var csharpTree = tree as CSharpSyntaxTree;
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
index b9c3ccce3ff2c1a8d2e3082dc92d58aecf85edb9..df13d26c199e75089da2d60f6e07cceefbbc067b 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
+++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs
@@ -61,6 +61,15 @@ internal class CSharpResources {
}
}
+ ///
+ /// Looks up a localized string similar to Cannot have a previousSubmission when not a submission..
+ ///
+ internal static string CannotHavePreviousSubmission {
+ get {
+ return ResourceManager.GetString("CannotHavePreviousSubmission", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Can't reference compilation of type '{0}' from {1} compilation..
///
@@ -88,6 +97,15 @@ internal class CSharpResources {
}
}
+ ///
+ /// Looks up a localized string similar to Could not find file..
+ ///
+ internal static string CouldNotFindFile {
+ get {
+ return ResourceManager.GetString("CouldNotFindFile", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to element is expected.
///
@@ -9692,6 +9710,15 @@ internal class CSharpResources {
}
}
+ ///
+ /// Looks up a localized string similar to SyntaxTree '{0}' resulted from a #load directive and cannot be removed or replaced directly..
+ ///
+ internal static string SyntaxTreeFromLoadNoRemoveReplace {
+ get {
+ return ResourceManager.GetString("SyntaxTreeFromLoadNoRemoveReplace", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to SyntaxTree '{0}' not found to remove.
///
@@ -9764,24 +9791,6 @@ internal class CSharpResources {
}
}
- ///
- /// Looks up a localized string similar to trees[{0}].
- ///
- internal static string Trees0 {
- get {
- return ResourceManager.GetString("Trees0", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to trees[{0}] must have root node with SyntaxKind.CompilationUnit..
- ///
- internal static string TreesMustHaveRootNode {
- get {
- return ResourceManager.GetString("TreesMustHaveRootNode", resourceCulture);
- }
- }
-
///
/// Looks up a localized string similar to Type argument cannot be null.
///
diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx
index b84724f3991de092c49c530278cc3a995c59423d..a8bf978e3de5fa8a5d85d7891e991dbb345b4d2c 100644
--- a/src/Compilers/CSharp/Portable/CSharpResources.resx
+++ b/src/Compilers/CSharp/Portable/CSharpResources.resx
@@ -4161,15 +4161,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
Can't reference compilation of type '{0}' from {1} compilation.
-
- trees[{0}] must have root node with SyntaxKind.CompilationUnit.
-
Syntax tree already present
-
- trees[{0}]
-
Submission can only include script code.
@@ -4647,4 +4641,14 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
Cannot use #load after first token in file
+
+ Could not find file.
+ File path referenced in source (#load) could not be resolved.
+
+
+ Cannot have a previousSubmission when not a submission.
+
+
+ SyntaxTree '{0}' resulted from a #load directive and cannot be removed or replaced directly.
+
\ No newline at end of file
diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
index 5493caeaecd44f989ff549cb1af9ed3082db396f..75b50d294d86902e4a18d3cb8c863d7adb0f2a64 100644
--- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
+++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
@@ -12,6 +12,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeGen;
+using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -44,9 +45,6 @@ public sealed partial class CSharpCompilation : Compilation
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;
@@ -101,6 +99,8 @@ internal Conversions Conversions
///
private ReferenceManager _referenceManager;
+ private readonly SyntaxAndDeclarationManager _syntaxAndDeclarations;
+
///
/// Contains the main method of this assembly, if there is one.
///
@@ -197,7 +197,7 @@ public override INamedTypeSymbol CreateErrorTypeSymbol(INamespaceOrTypeSymbol co
return Create(
assemblyName,
options ?? s_defaultOptions,
- (syntaxTrees != null) ? syntaxTrees.Cast() : null,
+ syntaxTrees,
references,
previousSubmission: null,
returnType: null,
@@ -250,16 +250,19 @@ public override INamedTypeSymbol CreateErrorTypeSymbol(INamespaceOrTypeSymbol co
assemblyName,
options,
validatedReferences,
- ImmutableArray.Empty,
- ImmutableDictionary.Create(ReferenceEqualityComparer.Instance),
- ImmutableDictionary.Create>(),
- DeclarationTable.Empty,
previousSubmission,
returnType,
hostObjectType,
isSubmission,
referenceManager: null,
- reuseReferenceManager: false);
+ reuseReferenceManager: false,
+ syntaxAndDeclarations: new SyntaxAndDeclarationManager(
+ ImmutableArray.Empty,
+ options.ScriptClassName,
+ options.SourceReferenceResolver,
+ CSharp.MessageProvider.Instance,
+ isSubmission,
+ state: null));
if (syntaxTrees != null)
{
@@ -274,35 +277,25 @@ public override INamedTypeSymbol CreateErrorTypeSymbol(INamespaceOrTypeSymbol co
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,
+ SyntaxAndDeclarationManager syntaxAndDeclarations,
AsyncQueue eventQueue = null)
- : base(assemblyName, references, submissionReturnType, hostObjectType, isSubmission, syntaxTreeOrdinalMap, eventQueue)
+ : base(assemblyName, references, SyntaxTreeCommonFeatures(syntaxAndDeclarations.ExternalSyntaxTrees), submissionReturnType, hostObjectType, isSubmission, eventQueue)
{
_wellKnownMemberSignatureComparer = new WellKnownMembersSignatureComparer(this);
_options = options;
- _syntaxTrees = syntaxTrees;
-
- _rootNamespaces = rootNamespaces;
- _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);
_scriptClass = new Lazy(BindScriptClass);
_globalImports = new Lazy(BindGlobalUsings);
_globalNamespaceAlias = new Lazy(CreateGlobalNamespaceAlias);
_anonymousTypeManager = new AnonymousTypeManager(this);
- this.LanguageVersion = CommonLanguageVersion(syntaxTrees);
+ this.LanguageVersion = CommonLanguageVersion(syntaxAndDeclarations.ExternalSyntaxTrees);
if (isSubmission)
{
@@ -324,10 +317,12 @@ public override INamedTypeSymbol CreateErrorTypeSymbol(INamespaceOrTypeSymbol co
{
_referenceManager = new ReferenceManager(
MakeSourceAssemblySimpleName(),
- options.AssemblyIdentityComparer,
- (referenceManager != null) ? referenceManager.ObservedMetadata : null);
+ this.Options.AssemblyIdentityComparer,
+ observedMetadata: referenceManager?.ObservedMetadata);
}
+ _syntaxAndDeclarations = syntaxAndDeclarations;
+
Debug.Assert((object)_lazyAssemblySymbol == null);
if (EventQueue != null) EventQueue.Enqueue(new CompilationStartedEvent(this));
}
@@ -363,7 +358,6 @@ private static LanguageVersion CommonLanguageVersion(ImmutableArray
return result ?? CSharpParseOptions.Default.LanguageVersion;
}
-
///
/// Create a duplicate of this compilation with different symbol instances.
///
@@ -373,39 +367,31 @@ public new CSharpCompilation Clone()
this.AssemblyName,
_options,
this.ExternalReferences,
- this.SyntaxTrees,
- this.syntaxTreeOrdinalMap,
- _rootNamespaces,
- _declarationTable,
_previousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
_referenceManager,
- reuseReferenceManager: true);
+ reuseReferenceManager: true,
+ syntaxAndDeclarations: _syntaxAndDeclarations);
}
- private CSharpCompilation UpdateSyntaxTrees(
- ImmutableArray syntaxTrees,
- ImmutableDictionary syntaxTreeOrdinalMap,
- ImmutableDictionary> rootNamespaces,
- DeclarationTable declarationTable,
- bool referenceDirectivesChanged)
+ private CSharpCompilation Update(
+ ReferenceManager referenceManager,
+ bool reuseReferenceManager,
+ SyntaxAndDeclarationManager syntaxAndDeclarations)
{
return new CSharpCompilation(
this.AssemblyName,
_options,
this.ExternalReferences,
- syntaxTrees,
- syntaxTreeOrdinalMap,
- rootNamespaces,
- declarationTable,
_previousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
- _referenceManager,
- reuseReferenceManager: !referenceDirectivesChanged);
+ referenceManager,
+ reuseReferenceManager,
+ syntaxAndDeclarations);
}
///
@@ -423,16 +409,13 @@ public new CSharpCompilation WithAssemblyName(string assemblyName)
assemblyName,
_options,
this.ExternalReferences,
- this.SyntaxTrees,
- this.syntaxTreeOrdinalMap,
- _rootNamespaces,
- _declarationTable,
_previousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
_referenceManager,
- reuseReferenceManager: assemblyName == this.AssemblyName);
+ reuseReferenceManager: assemblyName == this.AssemblyName,
+ syntaxAndDeclarations: _syntaxAndDeclarations);
}
///
@@ -455,16 +438,13 @@ public new CSharpCompilation WithReferences(IEnumerable refer
this.AssemblyName,
_options,
ValidateReferences(references),
- this.SyntaxTrees,
- this.syntaxTreeOrdinalMap,
- _rootNamespaces,
- _declarationTable,
_previousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
referenceManager: null,
- reuseReferenceManager: false);
+ reuseReferenceManager: false,
+ syntaxAndDeclarations: _syntaxAndDeclarations);
}
///
@@ -480,23 +460,30 @@ public new CSharpCompilation WithReferences(params MetadataReference[] reference
///
public CSharpCompilation WithOptions(CSharpCompilationOptions options)
{
- // Checks to see if the new options support reusing the reference manager
- bool reuseReferenceManager = this.Options.CanReuseCompilationReferenceManager(options);
+ var oldOptions = this.Options;
+ bool reuseReferenceManager = oldOptions.CanReuseCompilationReferenceManager(options);
+ bool reuseSyntaxAndDeclarationManager = oldOptions.ScriptClassName == options.ScriptClassName &&
+ oldOptions.SourceReferenceResolver == options.SourceReferenceResolver;
return new CSharpCompilation(
this.AssemblyName,
options,
this.ExternalReferences,
- _syntaxTrees,
- this.syntaxTreeOrdinalMap,
- _rootNamespaces,
- _declarationTable,
_previousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
_referenceManager,
- reuseReferenceManager);
+ reuseReferenceManager,
+ reuseSyntaxAndDeclarationManager ?
+ _syntaxAndDeclarations :
+ new SyntaxAndDeclarationManager(
+ _syntaxAndDeclarations.ExternalSyntaxTrees,
+ options.ScriptClassName,
+ options.SourceReferenceResolver,
+ _syntaxAndDeclarations.MessageProvider,
+ _syntaxAndDeclarations.IsSubmission,
+ state: null));
}
///
@@ -506,7 +493,7 @@ internal CSharpCompilation WithPreviousSubmission(CSharpCompilation newPreviousS
{
if (!this.IsSubmission)
{
- throw new NotSupportedException("Can't have a previousSubmission when not a submission");
+ throw new InvalidOperationException(CSharpResources.CannotHavePreviousSubmission);
}
// Reference binding doesn't depend on previous submission so we can reuse it.
@@ -515,16 +502,13 @@ internal CSharpCompilation WithPreviousSubmission(CSharpCompilation newPreviousS
this.AssemblyName,
_options,
this.ExternalReferences,
- this.SyntaxTrees,
- this.syntaxTreeOrdinalMap,
- _rootNamespaces,
- _declarationTable,
newPreviousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
_referenceManager,
- reuseReferenceManager: true);
+ reuseReferenceManager: true,
+ syntaxAndDeclarations: _syntaxAndDeclarations);
}
///
@@ -536,16 +520,13 @@ internal override Compilation WithEventQueue(AsyncQueue eventQ
this.AssemblyName,
_options,
this.ExternalReferences,
- this.SyntaxTrees,
- this.syntaxTreeOrdinalMap,
- _rootNamespaces,
- _declarationTable,
_previousSubmission,
this.SubmissionReturnType,
this.HostObjectType,
this.IsSubmission,
_referenceManager,
reuseReferenceManager: true,
+ syntaxAndDeclarations: _syntaxAndDeclarations,
eventQueue: eventQueue);
}
@@ -584,8 +565,8 @@ internal new TypeSymbol GetSubmissionResultType(out bool hasValue)
hasValue = false;
- // submission can be empty or comprise of a script file
- SyntaxTree tree = SyntaxTrees.SingleOrDefault();
+ // A submission may be empty or comprised of a single script file.
+ var tree = _syntaxAndDeclarations.ExternalSyntaxTrees.SingleOrDefault();
if (tree == null || tree.Options.Kind != SourceCodeKind.Interactive)
{
return GetSpecialType(SpecialType.System_Void);
@@ -619,7 +600,7 @@ internal new TypeSymbol GetSubmissionResultType(out bool hasValue)
///
public new ImmutableArray SyntaxTrees
{
- get { return _syntaxTrees; }
+ get { return _syntaxAndDeclarations.GetLazyState().SyntaxTrees; }
}
///
@@ -628,7 +609,7 @@ public new ImmutableArray SyntaxTrees
public new bool ContainsSyntaxTree(SyntaxTree syntaxTree)
{
var cstree = syntaxTree as SyntaxTree;
- return cstree != null && _rootNamespaces.ContainsKey((cstree));
+ return cstree != null && _syntaxAndDeclarations.GetLazyState().RootNamespaces.ContainsKey(cstree);
}
///
@@ -654,76 +635,52 @@ public new CSharpCompilation AddSyntaxTrees(IEnumerable trees)
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
+ // This HashSet is needed so that we don't allow adding the same tree twice
+ // with a single call to AddSyntaxTrees. Rather than using a separate HashSet,
+ // ReplaceSyntaxTrees can just check against ExternalSyntaxTrees, because we
+ // only allow replacing a single tree at a time.
+ var externalSyntaxTrees = PooledHashSet.GetInstance();
+ var syntaxAndDeclarations = _syntaxAndDeclarations;
+ externalSyntaxTrees.AddAll(syntaxAndDeclarations.ExternalSyntaxTrees);
+ bool reuseReferenceManager = true;
+ int i = 0;
+ foreach (var tree in trees.Cast())
{
- 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)
{
- 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));
- }
+ throw new ArgumentNullException($"{nameof(trees)}[{i}]");
+ }
- AddSyntaxTreeToDeclarationMapAndTable(tree, _options, IsSubmission, ref declMap, ref declTable, ref referenceDirectivesChanged);
- builder.Add(tree);
- ordinalMap = ordinalMap.Add(tree, oldTreeCount + i);
+ if (!tree.HasCompilationUnitRoot)
+ {
+ throw new ArgumentException(CSharpResources.TreeMustHaveARootNodeWith, $"{nameof(trees)}[{i}]");
+ }
- i++;
+ if (externalSyntaxTrees.Contains(tree))
+ {
+ throw new ArgumentException(CSharpResources.SyntaxTreeAlreadyPresent, $"{nameof(trees)}[{i}]");
}
- if (IsSubmission && declMap.Count > 1)
+ if (this.IsSubmission && tree.Options.Kind == SourceCodeKind.Regular)
{
- throw new ArgumentException(CSharpResources.SubmissionCanHaveAtMostOne, "trees");
+ throw new ArgumentException(CSharpResources.SubmissionCanOnlyInclude, $"{nameof(trees)}[{i}]");
}
- return UpdateSyntaxTrees(builder.ToImmutable(), ordinalMap, declMap, declTable, referenceDirectivesChanged);
+ externalSyntaxTrees.Add(tree);
+ reuseReferenceManager &= !tree.HasReferenceOrLoadDirectives;
+
+ i++;
}
- finally
+ externalSyntaxTrees.Free();
+
+ if (this.IsSubmission && i > 1)
{
- builder.Free();
+ throw new ArgumentException(CSharpResources.SubmissionCanHaveAtMostOne, nameof(trees));
}
- }
- 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.Add(tree, lazyRoot); // Callers are responsible for checking for existing entries.
- declTable = declTable.AddRootDeclaration(lazyRoot);
- referenceDirectivesChanged = referenceDirectivesChanged || tree.HasReferenceDirectives();
+ syntaxAndDeclarations = syntaxAndDeclarations.AddSyntaxTrees(trees);
+
+ return Update(_referenceManager, reuseReferenceManager, syntaxAndDeclarations);
}
///
@@ -751,51 +708,41 @@ public new CSharpCompilation RemoveSyntaxTrees(IEnumerable trees)
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();
+ var removeSet = PooledHashSet.GetInstance();
+ // This HashSet is needed so that we don't allow adding the same tree twice
+ // with a single call to AddSyntaxTrees. Rather than using a separate HashSet,
+ // ReplaceSyntaxTrees can just check against ExternalSyntaxTrees, because we
+ // only allow replacing a single tree at a time.
+ var externalSyntaxTrees = PooledHashSet.GetInstance();
+ var syntaxAndDeclarations = _syntaxAndDeclarations;
+ externalSyntaxTrees.AddAll(syntaxAndDeclarations.ExternalSyntaxTrees);
+ bool reuseReferenceManager = true;
int i = 0;
- foreach (var tree in this.SyntaxTrees)
+ foreach (var tree in trees.Cast())
{
- if (!removeSet.Contains(tree))
+ if (!externalSyntaxTrees.Contains(tree))
{
- builder.Add(tree);
- ordinalMap = ordinalMap.Add(tree, i++);
+ // Check to make sure this is not a #load'ed tree.
+ var loadedSyntaxTreeMap = syntaxAndDeclarations.GetLazyState().LoadedSyntaxTreeMap;
+ if (SyntaxAndDeclarationManager.IsLoadedSyntaxTree(tree, loadedSyntaxTreeMap))
+ {
+ throw new ArgumentException(string.Format(CSharpResources.SyntaxTreeFromLoadNoRemoveReplace, tree), $"{nameof(trees)}[{i}]");
+ }
+
+ throw new ArgumentException(string.Format(CSharpResources.SyntaxTreeNotFoundTo, tree), $"{nameof(trees)}[{i}]");
}
- }
- return UpdateSyntaxTrees(builder.ToImmutableAndFree(), ordinalMap, declMap, declTable, referenceDirectivesChanged);
- }
+ removeSet.Add(tree);
+ reuseReferenceManager &= !tree.HasReferenceOrLoadDirectives;
- 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");
+ i++;
}
+ externalSyntaxTrees.Free();
- declTable = declTable.RemoveRootDeclaration(lazyRoot);
- declMap = declMap.Remove(tree);
- referenceDirectivesChanged = referenceDirectivesChanged || tree.HasReferenceDirectives();
+ syntaxAndDeclarations = syntaxAndDeclarations.RemoveSyntaxTrees(removeSet);
+ removeSet.Free();
+
+ return Update(_referenceManager, reuseReferenceManager, syntaxAndDeclarations);
}
///
@@ -804,12 +751,11 @@ public new CSharpCompilation RemoveSyntaxTrees(IEnumerable trees)
///
public new CSharpCompilation RemoveAllSyntaxTrees()
{
- return UpdateSyntaxTrees(
- ImmutableArray.Empty,
- ImmutableDictionary.Create(),
- ImmutableDictionary.Create>(),
- DeclarationTable.Empty,
- referenceDirectivesChanged: _declarationTable.ReferenceDirectives.Any());
+ var syntaxAndDeclarations = _syntaxAndDeclarations;
+ return Update(
+ _referenceManager,
+ reuseReferenceManager: !syntaxAndDeclarations.MayHaveReferenceDirectives(),
+ syntaxAndDeclarations: syntaxAndDeclarations.WithExternalSyntaxTrees(ImmutableArray.Empty));
}
///
@@ -837,38 +783,41 @@ public new CSharpCompilation ReplaceSyntaxTree(SyntaxTree oldTree, SyntaxTree ne
if (!newTree.HasCompilationUnitRoot)
{
- throw new ArgumentException(CSharpResources.TreeMustHaveARootNodeWith, "newTree");
+ throw new ArgumentException(CSharpResources.TreeMustHaveARootNodeWith, nameof(newTree));
}
- var declMap = _rootNamespaces;
+ var syntaxAndDeclarations = _syntaxAndDeclarations;
+ var externalSyntaxTrees = syntaxAndDeclarations.ExternalSyntaxTrees;
+ if (!externalSyntaxTrees.Contains(oldTree))
+ {
+ // Check to see if this is a #load'ed tree.
+ var loadedSyntaxTreeMap = syntaxAndDeclarations.GetLazyState().LoadedSyntaxTreeMap;
+ if (SyntaxAndDeclarationManager.IsLoadedSyntaxTree(oldTree, loadedSyntaxTreeMap))
+ {
+ throw new ArgumentException(string.Format(CSharpResources.SyntaxTreeFromLoadNoRemoveReplace, oldTree), nameof(oldTree));
+ }
+
+ throw new ArgumentException(string.Format(CSharpResources.SyntaxTreeNotFoundTo, oldTree), nameof(oldTree));
+ }
- if (declMap.ContainsKey(newTree))
+ if (externalSyntaxTrees.Contains(newTree))
{
throw new ArgumentException(CSharpResources.SyntaxTreeAlreadyPresent, nameof(newTree));
}
- 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.
+ var reuseReferenceManager = !oldTree.HasReferenceOrLoadDirectives() && !newTree.HasReferenceOrLoadDirectives();
+ syntaxAndDeclarations = syntaxAndDeclarations.ReplaceSyntaxTree(oldTree, newTree);
- 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 Update(_referenceManager, reuseReferenceManager, syntaxAndDeclarations);
+ }
- return UpdateSyntaxTrees(newArray, ordinalMap, declMap, declTable, referenceDirectivesChanged);
+ internal override int GetSyntaxTreeOrdinal(SyntaxTree tree)
+ {
+ Debug.Assert(this.ContainsSyntaxTree(tree));
+ return _syntaxAndDeclarations.GetLazyState().OrdinalMap[tree];
}
#endregion
@@ -963,7 +912,7 @@ public override IEnumerable ReferencedAssemblyNames
///
internal override IEnumerable ReferenceDirectives
{
- get { return _declarationTable.ReferenceDirectives; }
+ get { return this.Declarations.ReferenceDirectives; }
}
///
@@ -1614,9 +1563,9 @@ public new SemanticModel GetSemanticModel(SyntaxTree syntaxTree, bool ignoreAcce
throw new ArgumentNullException(nameof(syntaxTree));
}
- if (!this.SyntaxTrees.Contains((SyntaxTree)syntaxTree))
+ if (!_syntaxAndDeclarations.GetLazyState().RootNamespaces.ContainsKey(syntaxTree))
{
- throw new ArgumentException("tree");
+ throw new ArgumentException(string.Format(CSharpResources.SyntaxTreeNotFoundTo, syntaxTree), nameof(syntaxTree));
}
return new SyntaxTreeSemanticModel(this, (SyntaxTree)syntaxTree, ignoreAccessibility);
@@ -1637,7 +1586,7 @@ internal BinderFactory GetBinderFactory(SyntaxTree syntaxTree)
var binderFactories = _binderFactories;
if (binderFactories == null)
{
- binderFactories = new WeakReference[_syntaxTrees.Length];
+ binderFactories = new WeakReference[this.SyntaxTrees.Length];
binderFactories = Interlocked.CompareExchange(ref _binderFactories, binderFactories, null) ?? binderFactories;
}
@@ -1702,11 +1651,11 @@ internal InteractiveUsingsBinder GetInteractiveUsingsBinder()
// empty compilation:
if ((object)ScriptClass == null)
{
- Debug.Assert(SyntaxTrees.Length == 0);
+ Debug.Assert(_syntaxAndDeclarations.ExternalSyntaxTrees.Length == 0);
return null;
}
- return GetBinderFactory(SyntaxTrees.Single()).GetInteractiveUsingsBinder();
+ return GetBinderFactory(_syntaxAndDeclarations.ExternalSyntaxTrees.Single()).GetInteractiveUsingsBinder();
}
private Imports BindGlobalUsings()
@@ -1730,7 +1679,7 @@ private void CompleteTree(SyntaxTree tree)
if (_lazyCompilationUnitCompletedTrees.Add(tree))
{
completedCompilationUnit = true;
- if (_lazyCompilationUnitCompletedTrees.Count == SyntaxTrees.Length)
+ if (_lazyCompilationUnitCompletedTrees.Count == this.SyntaxTrees.Length)
{
completedCompilation = true;
}
@@ -1783,7 +1732,7 @@ internal void ReportUnusedImports(DiagnosticBag diagnostics, CancellationToken c
}
else
{
- foreach (var tree in SyntaxTrees)
+ foreach (var tree in this.SyntaxTrees)
{
CompleteTree(tree);
}
@@ -1845,7 +1794,7 @@ public override int GetHashCode()
internal override CommonMessageProvider MessageProvider
{
- get { return CSharp.MessageProvider.Instance; }
+ get { return _syntaxAndDeclarations.MessageProvider; }
}
///
@@ -1897,7 +1846,7 @@ internal DeclarationTable Declarations
{
get
{
- return _declarationTable;
+ return _syntaxAndDeclarations.GetLazyState().DeclarationTable;
}
}
@@ -1942,19 +1891,28 @@ internal ImmutableArray GetDiagnostics(CompilationStage stage, bool
if (stage == CompilationStage.Parse || (stage > CompilationStage.Parse && includeEarlierStages))
{
+ var syntaxTrees = this.SyntaxTrees;
if (this.Options.ConcurrentBuild)
{
var parallelOptions = cancellationToken.CanBeCanceled
? new ParallelOptions() { CancellationToken = cancellationToken }
: DefaultParallelOptions;
- Parallel.For(0, this.SyntaxTrees.Length, parallelOptions,
- UICultureUtilities.WithCurrentUICulture(i => builder.AddRange(this.SyntaxTrees[i].GetDiagnostics(cancellationToken))));
+ Parallel.For(0, syntaxTrees.Length, parallelOptions,
+ UICultureUtilities.WithCurrentUICulture(i =>
+ {
+ var syntaxTree = syntaxTrees[i];
+ AppendLoadDirectiveDiagnostics(builder, _syntaxAndDeclarations, syntaxTree);
+ builder.AddRange(syntaxTree.GetDiagnostics(cancellationToken));
+ }));
}
else
{
- foreach (var syntaxTree in this.SyntaxTrees)
+ foreach (var syntaxTree in syntaxTrees)
{
+ cancellationToken.ThrowIfCancellationRequested();
+ AppendLoadDirectiveDiagnostics(builder, _syntaxAndDeclarations, syntaxTree);
+
cancellationToken.ThrowIfCancellationRequested();
builder.AddRange(syntaxTree.GetDiagnostics(cancellationToken));
}
@@ -1991,6 +1949,24 @@ internal ImmutableArray GetDiagnostics(CompilationStage stage, bool
return result.ToReadOnlyAndFree();
}
+ private static void AppendLoadDirectiveDiagnostics(DiagnosticBag builder, SyntaxAndDeclarationManager syntaxAndDeclarations, SyntaxTree syntaxTree, Func, IEnumerable> locationFilterOpt = null)
+ {
+ ImmutableArray loadDirectives;
+ if (syntaxAndDeclarations.GetLazyState().LoadDirectiveMap.TryGetValue(syntaxTree, out loadDirectives))
+ {
+ Debug.Assert(!loadDirectives.IsEmpty);
+ foreach (var directive in loadDirectives)
+ {
+ IEnumerable diagnostics = directive.Diagnostics;
+ if (locationFilterOpt != null)
+ {
+ diagnostics = locationFilterOpt(diagnostics);
+ }
+ builder.AddRange(diagnostics);
+ }
+ }
+ }
+
// Do the steps in compilation to get the method body diagnostics, but don't actually generate
// IL or emit an assembly.
private void GetDiagnosticsForAllMethodBodies(DiagnosticBag diagnostics, CancellationToken cancellationToken)
@@ -2172,6 +2148,9 @@ private static IEnumerable FilterDiagnosticsByLocation(IEnumerable CompilationStage.Parse && includeEarlierStages))
{
+ AppendLoadDirectiveDiagnostics(builder, _syntaxAndDeclarations, syntaxTree,
+ diagnostics => FilterDiagnosticsByLocation(diagnostics, syntaxTree, filterSpanWithinTree));
+
var syntaxDiagnostics = syntaxTree.GetDiagnostics();
syntaxDiagnostics = FilterDiagnosticsByLocation(syntaxDiagnostics, syntaxTree, filterSpanWithinTree);
builder.AddRange(syntaxDiagnostics);
@@ -2240,7 +2219,7 @@ protected override void AppendDefaultVersionResource(Stream resourceStream)
#region Emit
internal override byte LinkerMajorVersion => 0x30;
-
+
internal override bool IsDelaySigned
{
get { return SourceAssembly.IsDelaySigned; }
@@ -2410,9 +2389,11 @@ internal override StrongNameKeys StrongNameKeys
// TODO: consider unifying with VB
private bool StartSourceChecksumCalculation(PEModuleBuilder moduleBeingBuilt, DiagnosticBag diagnostics)
{
+ var syntaxTrees = this.SyntaxTrees;
+
// Check that all syntax trees are debuggable:
bool allTreesDebuggable = true;
- foreach (var tree in _syntaxTrees)
+ foreach (var tree in syntaxTrees)
{
if (!string.IsNullOrEmpty(tree.FilePath) && tree.GetText().Encoding == null)
{
@@ -2427,7 +2408,7 @@ private bool StartSourceChecksumCalculation(PEModuleBuilder moduleBeingBuilt, Di
}
// Add debug documents for all trees with distinct paths.
- foreach (var tree in _syntaxTrees)
+ foreach (var tree in syntaxTrees)
{
if (!string.IsNullOrEmpty(tree.FilePath))
{
@@ -2445,7 +2426,7 @@ private bool StartSourceChecksumCalculation(PEModuleBuilder moduleBeingBuilt, Di
// Add debug documents for all pragmas.
// If there are clashes with already processed directives, report warnings.
// If there are clashes with debug documents that came from actual trees, ignore the pragma.
- foreach (var tree in _syntaxTrees)
+ foreach (var tree in syntaxTrees)
{
AddDebugSourceDocumentsForChecksumDirectives(moduleBeingBuilt, tree, diagnostics);
}
@@ -2656,7 +2637,7 @@ private void SetupWin32Resources(PEModuleBuilder moduleBeingBuilt, Stream win32R
internal override bool HasCodeToEmit()
{
- foreach (var syntaxTree in SyntaxTrees)
+ foreach (var syntaxTree in this.SyntaxTrees)
{
var unit = syntaxTree.GetCompilationUnitRoot();
if (unit.Members.Count > 0)
@@ -2722,34 +2703,12 @@ protected override IEnumerable CommonSyntaxTrees
protected override Compilation CommonAddSyntaxTrees(IEnumerable trees)
{
- var array = trees as SyntaxTree[];
- if (array != null)
- {
- return this.AddSyntaxTrees(array);
- }
-
- if (trees == null)
- {
- throw new ArgumentNullException(nameof(trees));
- }
-
- return this.AddSyntaxTrees(trees.Cast());
+ return this.AddSyntaxTrees(trees);
}
protected override Compilation CommonRemoveSyntaxTrees(IEnumerable trees)
{
- var array = trees as SyntaxTree[];
- if (array != null)
- {
- return this.RemoveSyntaxTrees(array);
- }
-
- if (trees == null)
- {
- throw new ArgumentNullException(nameof(trees));
- }
-
- return this.RemoveSyntaxTrees(trees.Cast());
+ return this.RemoveSyntaxTrees(trees);
}
protected override Compilation CommonRemoveAllSyntaxTrees()
@@ -2879,7 +2838,7 @@ public override bool ContainsSymbolsWithName(Func predicate, Symbo
throw new ArgumentException(CSharpResources.NoNoneSearchCriteria, nameof(filter));
}
- return _declarationTable.ContainsName(predicate, filter, cancellationToken);
+ return this.Declarations.ContainsName(predicate, filter, cancellationToken);
}
///
@@ -2946,7 +2905,7 @@ public IEnumerable GetSymbolsWithName(Func predicate, Sym
var result = new HashSet();
var spine = new List();
- AppendSymbolsWithName(spine, _compilation._declarationTable.MergedRoot, predicate, filter, result, cancellationToken);
+ AppendSymbolsWithName(spine, _compilation.Declarations.MergedRoot, predicate, filter, result, cancellationToken);
return result;
}
diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxAndDeclarationManager.LazyState.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxAndDeclarationManager.LazyState.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ee22dc2eae37a42919d49f6edc2eb4d77ab782e2
--- /dev/null
+++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxAndDeclarationManager.LazyState.cs
@@ -0,0 +1,45 @@
+// 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.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using Microsoft.CodeAnalysis.Collections;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis.CSharp
+{
+ internal sealed partial class SyntaxAndDeclarationManager : CommonSyntaxAndDeclarationManager
+ {
+ internal sealed class State
+ {
+ internal readonly ImmutableArray SyntaxTrees; // In ordinal order.
+ internal readonly ImmutableDictionary OrdinalMap; // Inverse of syntaxTrees array (i.e. maps tree to index)
+ internal readonly ImmutableDictionary> LoadDirectiveMap;
+ internal readonly ImmutableDictionary LoadedSyntaxTreeMap;
+ internal readonly ImmutableDictionary> RootNamespaces;
+ internal readonly DeclarationTable DeclarationTable;
+
+ internal State(
+ ImmutableArray syntaxTrees,
+ ImmutableDictionary syntaxTreeOrdinalMap,
+ ImmutableDictionary> loadDirectiveMap,
+ ImmutableDictionary loadedSyntaxTreeMap,
+ ImmutableDictionary> rootNamespaces,
+ DeclarationTable declarationTable)
+ {
+ Debug.Assert(syntaxTrees.All(tree => syntaxTrees[syntaxTreeOrdinalMap[tree]] == tree));
+ Debug.Assert(syntaxTrees.SetEquals(rootNamespaces.Keys.AsImmutable(), EqualityComparer.Default));
+
+ this.SyntaxTrees = syntaxTrees;
+ this.OrdinalMap = syntaxTreeOrdinalMap;
+ this.LoadDirectiveMap = loadDirectiveMap;
+ this.LoadedSyntaxTreeMap = loadedSyntaxTreeMap;
+ this.RootNamespaces = rootNamespaces;
+ this.DeclarationTable = declarationTable;
+ }
+ }
+ }
+}
diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxAndDeclarationManager.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxAndDeclarationManager.cs
new file mode 100644
index 0000000000000000000000000000000000000000..93a8cbc30196c5052413f5fe6d9c7d4ddeb641e0
--- /dev/null
+++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxAndDeclarationManager.cs
@@ -0,0 +1,613 @@
+// 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.Diagnostics;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis.Collections;
+
+namespace Microsoft.CodeAnalysis.CSharp
+{
+ internal sealed partial class SyntaxAndDeclarationManager : CommonSyntaxAndDeclarationManager
+ {
+ private State _lazyState;
+
+ internal SyntaxAndDeclarationManager(
+ ImmutableArray externalSyntaxTrees,
+ string scriptClassName,
+ SourceReferenceResolver resolver,
+ CommonMessageProvider messageProvider,
+ bool isSubmission,
+ State state)
+ : base(externalSyntaxTrees, scriptClassName, resolver, messageProvider, isSubmission)
+ {
+ _lazyState = state;
+ }
+
+ internal State GetLazyState()
+ {
+ if (_lazyState == null)
+ {
+ Interlocked.CompareExchange(ref _lazyState, CreateState(this.ExternalSyntaxTrees, this.ScriptClassName, this.Resolver, this.MessageProvider, this.IsSubmission), null);
+ }
+
+ return _lazyState;
+ }
+
+ private static State CreateState(
+ ImmutableArray externalSyntaxTrees,
+ string scriptClassName,
+ SourceReferenceResolver resolver,
+ CommonMessageProvider messageProvider,
+ bool isSubmission)
+ {
+ var treesBuilder = ArrayBuilder.GetInstance();
+ var ordinalMapBuilder = PooledDictionary.GetInstance();
+ var loadDirectiveMapBuilder = PooledDictionary>.GetInstance();
+ var loadedSyntaxTreeMapBuilder = PooledDictionary.GetInstance();
+ var declMapBuilder = PooledDictionary>.GetInstance();
+ var declTable = DeclarationTable.Empty;
+
+ foreach (var tree in externalSyntaxTrees)
+ {
+ AppendAllSyntaxTrees(
+ treesBuilder,
+ tree,
+ scriptClassName,
+ resolver,
+ messageProvider,
+ isSubmission,
+ ordinalMapBuilder,
+ loadDirectiveMapBuilder,
+ loadedSyntaxTreeMapBuilder,
+ declMapBuilder,
+ ref declTable);
+ }
+
+ return new State(
+ treesBuilder.ToImmutableAndFree(),
+ ordinalMapBuilder.ToImmutableDictionaryAndFree(),
+ loadDirectiveMapBuilder.ToImmutableDictionaryAndFree(),
+ loadedSyntaxTreeMapBuilder.ToImmutableDictionaryAndFree(),
+ declMapBuilder.ToImmutableDictionaryAndFree(),
+ declTable);
+ }
+
+ public SyntaxAndDeclarationManager AddSyntaxTrees(IEnumerable trees)
+ {
+ var scriptClassName = this.ScriptClassName;
+ var resolver = this.Resolver;
+ var messageProvider = this.MessageProvider;
+ var isSubmission = this.IsSubmission;
+
+ var state = _lazyState;
+ var newExternalSyntaxTrees = this.ExternalSyntaxTrees.AddRange(trees);
+ if (state == null)
+ {
+ return this.WithExternalSyntaxTrees(newExternalSyntaxTrees);
+ }
+
+ var ordinalMapBuilder = state.OrdinalMap.ToBuilder();
+ var loadDirectiveMapBuilder = state.LoadDirectiveMap.ToBuilder();
+ var loadedSyntaxTreeMapBuilder = state.LoadedSyntaxTreeMap.ToBuilder();
+ var declMapBuilder = state.RootNamespaces.ToBuilder();
+ var declTable = state.DeclarationTable;
+
+ var treesBuilder = ArrayBuilder.GetInstance();
+ treesBuilder.AddRange(state.SyntaxTrees);
+
+ foreach (var tree in trees)
+ {
+ AppendAllSyntaxTrees(
+ treesBuilder,
+ tree,
+ scriptClassName,
+ resolver,
+ messageProvider,
+ isSubmission,
+ ordinalMapBuilder,
+ loadDirectiveMapBuilder,
+ loadedSyntaxTreeMapBuilder,
+ declMapBuilder,
+ ref declTable);
+ }
+
+ state = new State(
+ treesBuilder.ToImmutableAndFree(),
+ ordinalMapBuilder.ToImmutableDictionary(),
+ loadDirectiveMapBuilder.ToImmutableDictionary(),
+ loadedSyntaxTreeMapBuilder.ToImmutableDictionary(),
+ declMapBuilder.ToImmutableDictionary(),
+ declTable);
+
+ return new SyntaxAndDeclarationManager(
+ newExternalSyntaxTrees,
+ scriptClassName,
+ resolver,
+ messageProvider,
+ isSubmission,
+ state);
+ }
+
+ ///
+ /// Appends all trees (including any trees from #load'ed files).
+ ///
+ private static void AppendAllSyntaxTrees(
+ ArrayBuilder treesBuilder,
+ SyntaxTree tree,
+ string scriptClassName,
+ SourceReferenceResolver resolver,
+ CommonMessageProvider messageProvider,
+ bool isSubmission,
+ IDictionary ordinalMapBuilder,
+ IDictionary> loadDirectiveMapBuilder,
+ IDictionary loadedSyntaxTreeMapBuilder,
+ IDictionary> declMapBuilder,
+ ref DeclarationTable declTable)
+ {
+ var sourceCodeKind = tree.Options.Kind;
+ if (sourceCodeKind == SourceCodeKind.Interactive || sourceCodeKind == SourceCodeKind.Script)
+ {
+ AppendAllLoadedSyntaxTrees(treesBuilder, tree, scriptClassName, resolver, messageProvider, isSubmission, ordinalMapBuilder, loadDirectiveMapBuilder, loadedSyntaxTreeMapBuilder, declMapBuilder, ref declTable);
+ }
+
+ AddSyntaxTreeToDeclarationMapAndTable(tree, scriptClassName, isSubmission, declMapBuilder, ref declTable);
+
+ treesBuilder.Add(tree);
+
+ ordinalMapBuilder.Add(tree, ordinalMapBuilder.Count);
+ }
+
+ private static void AppendAllLoadedSyntaxTrees(
+ ArrayBuilder treesBuilder,
+ SyntaxTree tree,
+ string scriptClassName,
+ SourceReferenceResolver resolver,
+ CommonMessageProvider messageProvider,
+ bool isSubmission,
+ IDictionary ordinalMapBuilder,
+ IDictionary> loadDirectiveMapBuilder,
+ IDictionary loadedSyntaxTreeMapBuilder,
+ IDictionary> declMapBuilder,
+ ref DeclarationTable declTable)
+ {
+ ArrayBuilder loadDirectives = null;
+
+ foreach (var directive in tree.GetCompilationUnitRoot().GetLoadDirectives())
+ {
+ var fileToken = directive.File;
+ var path = (string)fileToken.Value;
+ if (path == null)
+ {
+ // If there is no path, the parser should have some Diagnostics to report.
+ Debug.Assert(tree.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error));
+ continue;
+ }
+
+ var diagnostics = DiagnosticBag.GetInstance();
+ var resolvedFilePath = resolver.ResolveReference(path, baseFilePath: tree.FilePath);
+ if (resolvedFilePath == null)
+ {
+ diagnostics.Add(
+ messageProvider.CreateDiagnostic(
+ (int)ErrorCode.ERR_NoSourceFile,
+ fileToken.GetLocation(),
+ path,
+ CSharpResources.CouldNotFindFile));
+ }
+ else if (!loadedSyntaxTreeMapBuilder.ContainsKey(resolvedFilePath))
+ {
+ try
+ {
+ var code = resolver.ReadText(resolvedFilePath);
+ var loadedTree = SyntaxFactory.ParseSyntaxTree(
+ code,
+ tree.Options, // Use ParseOptions propagated from "external" tree.
+ resolvedFilePath);
+
+ // All #load'ed trees should have unique path information.
+ loadedSyntaxTreeMapBuilder.Add(loadedTree.FilePath, loadedTree);
+
+ AppendAllSyntaxTrees(
+ treesBuilder,
+ loadedTree,
+ scriptClassName,
+ resolver,
+ messageProvider,
+ isSubmission,
+ ordinalMapBuilder,
+ loadDirectiveMapBuilder,
+ loadedSyntaxTreeMapBuilder,
+ declMapBuilder,
+ ref declTable);
+ }
+ catch (Exception e)
+ {
+ diagnostics.Add(
+ CommonCompiler.ToFileReadDiagnostics(messageProvider, e, resolvedFilePath),
+ fileToken.GetLocation());
+ }
+ }
+ else
+ {
+ // The path resolved, but we've seen this file before,
+ // so don't attempt to load it again.
+ Debug.Assert(diagnostics.IsEmptyWithoutResolution);
+ }
+
+ if (loadDirectives == null)
+ {
+ loadDirectives = ArrayBuilder.GetInstance();
+ }
+ loadDirectives.Add(new LoadDirective(resolvedFilePath, diagnostics.ToReadOnlyAndFree()));
+ }
+
+ if (loadDirectives != null)
+ {
+ loadDirectiveMapBuilder.Add(tree, loadDirectives.ToImmutableAndFree());
+ }
+ }
+
+ private static void AddSyntaxTreeToDeclarationMapAndTable(
+ SyntaxTree tree,
+ string scriptClassName,
+ bool isSubmission,
+ IDictionary> declMapBuilder,
+ ref DeclarationTable declTable)
+ {
+ var lazyRoot = new Lazy(() => DeclarationTreeBuilder.ForTree(tree, scriptClassName, isSubmission));
+ declMapBuilder.Add(tree, lazyRoot); // Callers are responsible for checking for existing entries.
+ declTable = declTable.AddRootDeclaration(lazyRoot);
+ }
+
+ public SyntaxAndDeclarationManager RemoveSyntaxTrees(HashSet trees)
+ {
+ var state = _lazyState;
+ var newExternalSyntaxTrees = this.ExternalSyntaxTrees.RemoveAll(t => trees.Contains(t));
+ if (state == null)
+ {
+ return this.WithExternalSyntaxTrees(newExternalSyntaxTrees);
+ }
+
+ var syntaxTrees = state.SyntaxTrees;
+ var loadDirectiveMap = state.LoadDirectiveMap;
+ var loadedSyntaxTreeMap = state.LoadedSyntaxTreeMap;
+ var removeSet = PooledHashSet.GetInstance();
+ foreach (var tree in trees)
+ {
+ int unused1;
+ ImmutableArray unused2;
+ GetRemoveSet(
+ tree,
+ includeLoadedTrees: true,
+ syntaxTrees: syntaxTrees,
+ syntaxTreeOrdinalMap: state.OrdinalMap,
+ loadDirectiveMap: loadDirectiveMap,
+ loadedSyntaxTreeMap: loadedSyntaxTreeMap,
+ removeSet: removeSet,
+ totalReferencedTreeCount: out unused1,
+ oldLoadDirectives: out unused2);
+ }
+
+ var treesBuilder = ArrayBuilder.GetInstance();
+ var ordinalMapBuilder = PooledDictionary.GetInstance();
+ var declMapBuilder = state.RootNamespaces.ToBuilder();
+ var declTable = state.DeclarationTable;
+ foreach (var tree in syntaxTrees)
+ {
+ if (removeSet.Contains(tree))
+ {
+ loadDirectiveMap = loadDirectiveMap.Remove(tree);
+ loadedSyntaxTreeMap = loadedSyntaxTreeMap.Remove(tree.FilePath);
+ RemoveSyntaxTreeFromDeclarationMapAndTable(tree, declMapBuilder, ref declTable);
+ }
+ else if (!IsLoadedSyntaxTree(tree, loadedSyntaxTreeMap))
+ {
+ UpdateSyntaxTreesAndOrdinalMapOnly(
+ treesBuilder,
+ tree,
+ ordinalMapBuilder,
+ loadDirectiveMap,
+ loadedSyntaxTreeMap);
+ }
+ }
+ removeSet.Free();
+
+ state = new State(
+ treesBuilder.ToImmutableAndFree(),
+ ordinalMapBuilder.ToImmutableDictionaryAndFree(),
+ loadDirectiveMap,
+ loadedSyntaxTreeMap,
+ declMapBuilder.ToImmutableDictionary(),
+ declTable);
+
+ return new SyntaxAndDeclarationManager(
+ newExternalSyntaxTrees,
+ this.ScriptClassName,
+ this.Resolver,
+ this.MessageProvider,
+ this.IsSubmission,
+ state);
+ }
+
+ ///
+ /// Collects all the trees #load'ed by (as well as
+ /// itself) and populates
+ /// with all the trees that are safe to remove (not #load'ed by any other tree).
+ ///
+ private static void GetRemoveSet(
+ SyntaxTree oldTree,
+ bool includeLoadedTrees,
+ ImmutableArray syntaxTrees,
+ ImmutableDictionary syntaxTreeOrdinalMap,
+ ImmutableDictionary> loadDirectiveMap,
+ ImmutableDictionary loadedSyntaxTreeMap,
+ HashSet removeSet,
+ out int totalReferencedTreeCount,
+ out ImmutableArray oldLoadDirectives)
+ {
+ if (includeLoadedTrees && loadDirectiveMap.TryGetValue(oldTree, out oldLoadDirectives))
+ {
+ Debug.Assert(!oldLoadDirectives.IsEmpty);
+ GetRemoveSetForLoadedTrees(oldLoadDirectives, loadDirectiveMap, loadedSyntaxTreeMap, removeSet);
+ }
+
+ removeSet.Add(oldTree);
+ totalReferencedTreeCount = removeSet.Count;
+
+ // Check subsequent trees to see if they #load any of the trees we're about
+ // to remove. We don't want to remove a tree until the last external tree
+ // that depends on it is removed.
+ if (removeSet.Count > 1)
+ {
+ var ordinal = syntaxTreeOrdinalMap[oldTree];
+ for (int i = ordinal + 1; i < syntaxTrees.Length; i++)
+ {
+ var tree = syntaxTrees[i];
+ ImmutableArray loadDirectives;
+ if (loadDirectiveMap.TryGetValue(tree, out loadDirectives))
+ {
+ Debug.Assert(!loadDirectives.IsEmpty);
+ foreach (var directive in loadDirectives)
+ {
+ var loadedTree = loadedSyntaxTreeMap[directive.ResolvedPath];
+ if (removeSet.Contains(loadedTree))
+ {
+ removeSet.Remove(loadedTree);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static void GetRemoveSetForLoadedTrees(
+ ImmutableArray loadDirectives,
+ ImmutableDictionary> loadDirectiveMap,
+ ImmutableDictionary loadedSyntaxTreeMap,
+ HashSet removeSet)
+ {
+ foreach (var directive in loadDirectives)
+ {
+ if (directive.ResolvedPath != null)
+ {
+ var loadedTree = loadedSyntaxTreeMap[directive.ResolvedPath];
+ ImmutableArray nestedLoadDirectives;
+ if (loadDirectiveMap.TryGetValue(loadedTree, out nestedLoadDirectives))
+ {
+ Debug.Assert(!nestedLoadDirectives.IsEmpty);
+ GetRemoveSetForLoadedTrees(nestedLoadDirectives, loadDirectiveMap, loadedSyntaxTreeMap, removeSet);
+ }
+
+ removeSet.Add(loadedTree);
+ }
+ }
+ }
+
+ private static void RemoveSyntaxTreeFromDeclarationMapAndTable(
+ SyntaxTree tree,
+ IDictionary> declMap,
+ ref DeclarationTable declTable)
+ {
+ var lazyRoot = declMap[tree];
+ declTable = declTable.RemoveRootDeclaration(lazyRoot);
+ declMap.Remove(tree);
+ }
+
+ public SyntaxAndDeclarationManager ReplaceSyntaxTree(SyntaxTree oldTree, SyntaxTree newTree)
+ {
+ var state = _lazyState;
+ var newExternalSyntaxTrees = this.ExternalSyntaxTrees.Replace(oldTree, newTree);
+ if (state == null)
+ {
+ return this.WithExternalSyntaxTrees(newExternalSyntaxTrees);
+ }
+
+ var newLoadDirectivesSyntax = newTree.GetCompilationUnitRoot().GetLoadDirectives();
+ var loadDirectivesHaveChanged = !oldTree.GetCompilationUnitRoot().GetLoadDirectives().SequenceEqual(newLoadDirectivesSyntax);
+ var syntaxTrees = state.SyntaxTrees;
+ var ordinalMap = state.OrdinalMap;
+ var loadDirectiveMap = state.LoadDirectiveMap;
+ var loadedSyntaxTreeMap = state.LoadedSyntaxTreeMap;
+ var removeSet = PooledHashSet.GetInstance();
+ int totalReferencedTreeCount;
+ ImmutableArray oldLoadDirectives;
+ GetRemoveSet(
+ oldTree,
+ loadDirectivesHaveChanged,
+ syntaxTrees,
+ ordinalMap,
+ loadDirectiveMap,
+ loadedSyntaxTreeMap,
+ removeSet,
+ out totalReferencedTreeCount,
+ out oldLoadDirectives);
+
+ var loadDirectiveMapBuilder = loadDirectiveMap.ToBuilder();
+ var loadedSyntaxTreeMapBuilder = loadedSyntaxTreeMap.ToBuilder();
+ var declMapBuilder = state.RootNamespaces.ToBuilder();
+ var declTable = state.DeclarationTable;
+ foreach (var tree in removeSet)
+ {
+ loadDirectiveMapBuilder.Remove(tree);
+ loadedSyntaxTreeMapBuilder.Remove(tree.FilePath);
+ RemoveSyntaxTreeFromDeclarationMapAndTable(tree, declMapBuilder, ref declTable);
+ }
+ removeSet.Free();
+
+ var oldOrdinal = ordinalMap[oldTree];
+ ImmutableArray newTrees;
+ if (loadDirectivesHaveChanged)
+ {
+ // Should have been removed above...
+ Debug.Assert(!loadDirectiveMapBuilder.ContainsKey(oldTree));
+ Debug.Assert(!loadDirectiveMapBuilder.ContainsKey(newTree));
+
+ // If we're inserting new #load'ed trees, we'll rebuild
+ // the whole syntaxTree array and the ordinalMap.
+ var treesBuilder = ArrayBuilder.GetInstance();
+ var ordinalMapBuilder = PooledDictionary.GetInstance();
+ for (var i = 0; i <= (oldOrdinal - totalReferencedTreeCount); i++)
+ {
+ var tree = syntaxTrees[i];
+ treesBuilder.Add(tree);
+ ordinalMapBuilder.Add(tree, i);
+ }
+
+ AppendAllSyntaxTrees(
+ treesBuilder,
+ newTree,
+ this.ScriptClassName,
+ this.Resolver,
+ this.MessageProvider,
+ this.IsSubmission,
+ ordinalMapBuilder,
+ loadDirectiveMapBuilder,
+ loadedSyntaxTreeMapBuilder,
+ declMapBuilder,
+ ref declTable);
+
+ for (var i = oldOrdinal + 1; i < syntaxTrees.Length; i++)
+ {
+ var tree = syntaxTrees[i];
+ if (!IsLoadedSyntaxTree(tree, loadedSyntaxTreeMap))
+ {
+ UpdateSyntaxTreesAndOrdinalMapOnly(
+ treesBuilder,
+ tree,
+ ordinalMapBuilder,
+ loadDirectiveMap,
+ loadedSyntaxTreeMap);
+ }
+ }
+
+ newTrees = treesBuilder.ToImmutableAndFree();
+ ordinalMap = ordinalMapBuilder.ToImmutableDictionaryAndFree();
+ Debug.Assert(newTrees.Length == ordinalMap.Count);
+ }
+ else
+ {
+ AddSyntaxTreeToDeclarationMapAndTable(newTree, this.ScriptClassName, this.IsSubmission, declMapBuilder, ref declTable);
+
+ if (newLoadDirectivesSyntax.Any())
+ {
+ // If load directives have not changed and there are new directives,
+ // then there should have been (matching) old directives as well.
+ Debug.Assert(!oldLoadDirectives.IsDefault);
+ Debug.Assert(!oldLoadDirectives.IsEmpty);
+ Debug.Assert(oldLoadDirectives.Length == newLoadDirectivesSyntax.Count);
+ loadDirectiveMapBuilder[newTree] = oldLoadDirectives;
+ }
+
+ Debug.Assert(ordinalMap.ContainsKey(oldTree)); // Checked by RemoveSyntaxTreeFromDeclarationMapAndTable
+
+ newTrees = syntaxTrees.SetItem(oldOrdinal, newTree);
+
+ ordinalMap = ordinalMap.Remove(oldTree);
+ ordinalMap = ordinalMap.SetItem(newTree, oldOrdinal);
+ }
+
+ state = new State(
+ newTrees,
+ ordinalMap,
+ loadDirectiveMapBuilder.ToImmutable(),
+ loadedSyntaxTreeMapBuilder.ToImmutable(),
+ declMapBuilder.ToImmutable(),
+ declTable);
+
+ return new SyntaxAndDeclarationManager(
+ newExternalSyntaxTrees,
+ this.ScriptClassName,
+ this.Resolver,
+ this.MessageProvider,
+ this.IsSubmission,
+ state);
+ }
+
+ internal SyntaxAndDeclarationManager WithExternalSyntaxTrees(ImmutableArray trees)
+ {
+ return new SyntaxAndDeclarationManager(trees, this.ScriptClassName, this.Resolver, this.MessageProvider, this.IsSubmission, state: null);
+ }
+
+ internal static bool IsLoadedSyntaxTree(SyntaxTree tree, ImmutableDictionary loadedSyntaxTreeMap)
+ {
+ SyntaxTree loadedTree;
+ return loadedSyntaxTreeMap.TryGetValue(tree.FilePath, out loadedTree) && (tree == loadedTree);
+ }
+
+ private static void UpdateSyntaxTreesAndOrdinalMapOnly(
+ ArrayBuilder treesBuilder,
+ SyntaxTree tree,
+ IDictionary ordinalMapBuilder,
+ ImmutableDictionary> loadDirectiveMap,
+ ImmutableDictionary loadedSyntaxTreeMap)
+ {
+ var sourceCodeKind = tree.Options.Kind;
+ if (sourceCodeKind == SourceCodeKind.Interactive || sourceCodeKind == SourceCodeKind.Script)
+ {
+ ImmutableArray loadDirectives;
+ if (loadDirectiveMap.TryGetValue(tree, out loadDirectives))
+ {
+ Debug.Assert(!loadDirectives.IsEmpty);
+ foreach (var directive in loadDirectives)
+ {
+ var resolvedPath = directive.ResolvedPath;
+ Debug.Assert((resolvedPath != null) || !directive.Diagnostics.IsEmpty);
+ if (resolvedPath != null)
+ {
+ var loadedTree = loadedSyntaxTreeMap[directive.ResolvedPath];
+ UpdateSyntaxTreesAndOrdinalMapOnly(
+ treesBuilder,
+ loadedTree,
+ ordinalMapBuilder,
+ loadDirectiveMap,
+ loadedSyntaxTreeMap);
+ }
+
+ treesBuilder.Add(tree);
+
+ ordinalMapBuilder.Add(tree, ordinalMapBuilder.Count);
+ }
+ }
+ }
+
+ treesBuilder.Add(tree);
+
+ ordinalMapBuilder.Add(tree, ordinalMapBuilder.Count);
+ }
+
+ internal bool MayHaveReferenceDirectives()
+ {
+ var state = _lazyState;
+ if (state == null)
+ {
+ var externalSyntaxTrees = this.ExternalSyntaxTrees;
+ return externalSyntaxTrees.Any(t => t.HasReferenceOrLoadDirectives());
+ }
+
+ return state.DeclarationTable.ReferenceDirectives.Any();
+ }
+ }
+}
diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.cs
index e6250699fde3cea0de86e83d4aec8495e9e58cb6..3f66fb0a2fcf497caa468472be9ff782bce31d13 100644
--- a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.cs
+++ b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.cs
@@ -110,6 +110,22 @@ internal bool HasReferenceDirectives
}
}
+ internal bool HasReferenceOrLoadDirectives
+ {
+ get
+ {
+ Debug.Assert(this.HasCompilationUnitRoot);
+
+ if (Options.Kind == SourceCodeKind.Interactive || Options.Kind == SourceCodeKind.Script)
+ {
+ var compilationUnitRoot = this.GetCompilationUnitRoot();
+ return compilationUnitRoot.GetReferenceDirectives().Count > 0 || compilationUnitRoot.GetLoadDirectives().Count > 0;
+ }
+
+ return false;
+ }
+ }
+
#region Preprocessor Symbols
private bool _hasDirectives;
private InternalSyntax.DirectiveStack _directives;
diff --git a/src/Compilers/CSharp/Portable/Syntax/CompilationUnitSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/CompilationUnitSyntax.cs
index 22284bda836d88fa129a762185385c1307f1ce0c..97722f40474b9dcd8960f9e97a44e3826d5b4b6c 100644
--- a/src/Compilers/CSharp/Portable/Syntax/CompilationUnitSyntax.cs
+++ b/src/Compilers/CSharp/Portable/Syntax/CompilationUnitSyntax.cs
@@ -26,7 +26,7 @@ internal IList GetReferenceDirectives(Func
- /// Returns #r directives specified in the compilation.
+ /// Returns #load directives specified in the compilation.
///
public IList GetLoadDirectives()
{
diff --git a/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj b/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj
index efbdaeac797237d2488c397148572967293a7ed5..6b31c9534fe03c41817723b5e861041adf4555a8 100644
--- a/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj
+++ b/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj
@@ -68,6 +68,7 @@
+
diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/LoadDirectiveTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/LoadDirectiveTests.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a7bfbcce1b302104685ba23c5a35efe33c20cc88
--- /dev/null
+++ b/src/Compilers/CSharp/Test/Symbol/Compilation/LoadDirectiveTests.cs
@@ -0,0 +1,133 @@
+// 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.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
+using Xunit;
+
+namespace Microsoft.CodeAnalysis.CSharp.UnitTests
+{
+ public class LoadDirectiveTests : CSharpTestBase
+ {
+ [Fact]
+ void EmptyFile()
+ {
+ var code = "#load \"\"";
+ var compilation = CreateCompilation(code);
+
+ Assert.Single(compilation.SyntaxTrees);
+ compilation.GetDiagnostics().Verify(
+ // error CS1504: Source file '' could not be opened -- Could not find file.
+ Diagnostic(ErrorCode.ERR_NoSourceFile, "\"\"").WithArguments("", CSharpResources.CouldNotFindFile).WithLocation(1, 7));
+ }
+
+ [Fact]
+ void MissingFile()
+ {
+ var code = "#load \"missing\"";
+ var compilation = CreateCompilation(code);
+
+ Assert.Single(compilation.SyntaxTrees);
+ compilation.GetDiagnostics().Verify(
+ // error CS1504: Source file 'missing' could not be opened -- Could not find file.
+ Diagnostic(ErrorCode.ERR_NoSourceFile, "\"missing\"").WithArguments("missing", CSharpResources.CouldNotFindFile).WithLocation(1, 7));
+ }
+
+ [Fact]
+ void FileWithErrors()
+ {
+ var code = "#load \"a.csx\"";
+ var resolver = CreateResolver(
+ Script("a.csx", @"
+ #load ""b.csx""
+ asdf();"));
+ var compilation = CreateCompilation(code, resolver);
+
+ Assert.Equal(2, compilation.SyntaxTrees.Length);
+ compilation.GetParseDiagnostics().Verify(
+ // a.csx(2,27): error CS1504: Source file 'b.csx' could not be opened -- Could not find file.
+ // #load "b.csx";
+ Diagnostic(ErrorCode.ERR_NoSourceFile, @"""b.csx""").WithArguments("b.csx", "Could not find file.").WithLocation(2, 27));
+ compilation.GetDiagnostics().Verify(
+ // a.csx(2,27): error CS1504: Source file 'b.csx' could not be opened -- Could not find file.
+ // #load "b.csx";
+ Diagnostic(ErrorCode.ERR_NoSourceFile, @"""b.csx""").WithArguments("b.csx", "Could not find file.").WithLocation(2, 27),
+ // a.csx(3,21): error CS0103: The name 'asdf' does not exist in the current context
+ // asdf();
+ Diagnostic(ErrorCode.ERR_NameNotInContext, "asdf").WithArguments("asdf").WithLocation(3, 21));
+ }
+
+ private static CSharpCompilation CreateCompilation(string code, SourceReferenceResolver sourceReferenceResolver = null)
+ {
+ var options = new CSharpCompilationOptions(
+ OutputKind.DynamicallyLinkedLibrary,
+ sourceReferenceResolver: sourceReferenceResolver ?? TestSourceReferenceResolver.Default);
+ var parseOptions = new CSharpParseOptions(kind: SourceCodeKind.Interactive);
+ return CreateCompilationWithMscorlib(code, options: options, parseOptions: parseOptions);
+ }
+
+ private static SourceReferenceResolver CreateResolver(params KeyValuePair[] scripts)
+ {
+ var sources = new Dictionary();
+ foreach (var script in scripts)
+ {
+ sources.Add(script.Key, script.Value);
+ }
+ return new TestSourceReferenceResolver(sources);
+ }
+
+ private static KeyValuePair Script(string path, string source)
+ {
+ return new KeyValuePair(path, source);
+ }
+
+ private class TestSourceReferenceResolver : SourceReferenceResolver
+ {
+ private readonly IDictionary _sources;
+
+ public static TestSourceReferenceResolver Default { get; } = new TestSourceReferenceResolver();
+
+ public TestSourceReferenceResolver(IDictionary sources = null)
+ {
+ _sources = sources;
+ }
+
+ public override string NormalizePath(string path, string baseFilePath)
+ {
+ return path;
+ }
+
+ public override string ResolveReference(string path, string baseFilePath)
+ {
+ return ((_sources != null) && _sources.ContainsKey(path)) ? path : null;
+ }
+
+ public override Stream OpenRead(string resolvedPath)
+ {
+ if (_sources != null)
+ {
+ return new MemoryStream(Encoding.UTF8.GetBytes(_sources[resolvedPath]));
+ }
+ else
+ {
+ throw new IOException();
+ }
+ }
+
+ public override bool Equals(object other)
+ {
+ return this.Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return this.GetHashCode();
+ }
+ }
+ }
+}
diff --git a/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/PreprocessorTests.cs b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/PreprocessorTests.cs
index 353bd09d94e8471da443b3da3dd3dc73b39a0e99..01e45ed294ae789d6675c5a5ab800da6cbb36fd5 100644
--- a/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/PreprocessorTests.cs
+++ b/src/Compilers/CSharp/Test/Syntax/LexicalAndXml/PreprocessorTests.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
diff --git a/src/Compilers/Core/Portable/CodeAnalysis.csproj b/src/Compilers/Core/Portable/CodeAnalysis.csproj
index 64a34677a995c84da566f5ecd5456a812687e637..1b0f114060a7b02639ea6f7602c95b6c3f59d4df 100644
--- a/src/Compilers/Core/Portable/CodeAnalysis.csproj
+++ b/src/Compilers/Core/Portable/CodeAnalysis.csproj
@@ -135,6 +135,8 @@
+
+
diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs
index 2c1c1e5ba903f46091ce8030f215bdebe4adcd9e..596c5887ae30cf0904fc7b055b9b7ecbd4144656 100644
--- a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs
+++ b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs
@@ -150,40 +150,46 @@ internal SourceText ReadFileContent(CommandLineSourceFile file, IListFile content or null on failure.
internal SourceText ReadFileContent(CommandLineSourceFile file, IList diagnostics, Encoding encoding, SourceHashAlgorithm checksumAlgorithm, out string normalizedFilePath)
{
+ var filePath = file.Path;
try
{
- // PERF: Using a very small buffer size for the FileStream opens up an optimization within EncodedStringText where
- // we read the entire FileStream into a byte array in one shot. For files that are actually smaller than the buffer
- // size, FileStream.Read still allocates the internal buffer.
- using (var data = PortableShim.FileStream.Create(file.Path, PortableShim.FileMode.Open, PortableShim.FileAccess.Read, PortableShim.FileShare.ReadWrite, bufferSize: 1, options: PortableShim.FileOptions.None))
- {
- normalizedFilePath = (string)PortableShim.FileStream.Name.GetValue(data);
- return EncodedStringText.Create(data, encoding, checksumAlgorithm);
- }
+ return ReadFileContentHelper(filePath, encoding, checksumAlgorithm, out normalizedFilePath);
}
catch (Exception e)
{
- diagnostics.Add(ToFileReadDiagnostics(e, file));
+ diagnostics.Add(ToFileReadDiagnostics(this.MessageProvider, e, filePath));
normalizedFilePath = null;
return null;
}
}
- private DiagnosticInfo ToFileReadDiagnostics(Exception e, CommandLineSourceFile file)
+ internal static SourceText ReadFileContentHelper(string filePath, Encoding encoding, SourceHashAlgorithm checksumAlgorithm, out string normalizedFilePath)
+ {
+ // PERF: Using a very small buffer size for the FileStream opens up an optimization within EncodedStringText where
+ // we read the entire FileStream into a byte array in one shot. For files that are actually smaller than the buffer
+ // size, FileStream.Read still allocates the internal buffer.
+ using (var data = PortableShim.FileStream.Create(filePath, PortableShim.FileMode.Open, PortableShim.FileAccess.Read, PortableShim.FileShare.ReadWrite, bufferSize: 1, options: PortableShim.FileOptions.None))
+ {
+ normalizedFilePath = (string)PortableShim.FileStream.Name.GetValue(data);
+ return EncodedStringText.Create(data, encoding, checksumAlgorithm);
+ }
+ }
+
+ internal static DiagnosticInfo ToFileReadDiagnostics(CommonMessageProvider messageProvider, Exception e, string filePath)
{
DiagnosticInfo diagnosticInfo;
if (e is FileNotFoundException || e.GetType().Name == "DirectoryNotFoundException")
{
- diagnosticInfo = new DiagnosticInfo(MessageProvider, MessageProvider.ERR_FileNotFound, file.Path);
+ diagnosticInfo = new DiagnosticInfo(messageProvider, messageProvider.ERR_FileNotFound, filePath);
}
else if (e is InvalidDataException)
{
- diagnosticInfo = new DiagnosticInfo(MessageProvider, MessageProvider.ERR_BinaryFile, file.Path);
+ diagnosticInfo = new DiagnosticInfo(messageProvider, messageProvider.ERR_BinaryFile, filePath);
}
else
{
- diagnosticInfo = new DiagnosticInfo(MessageProvider, MessageProvider.ERR_NoSourceFile, file.Path, e.Message);
+ diagnosticInfo = new DiagnosticInfo(messageProvider, messageProvider.ERR_NoSourceFile, filePath, e.Message);
}
return diagnosticInfo;
diff --git a/src/Compilers/Core/Portable/Compilation/CommonSyntaxAndDeclarationManager.cs b/src/Compilers/Core/Portable/Compilation/CommonSyntaxAndDeclarationManager.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4987ccd943e37470b057d9cc3ec69084b1535421
--- /dev/null
+++ b/src/Compilers/Core/Portable/Compilation/CommonSyntaxAndDeclarationManager.cs
@@ -0,0 +1,38 @@
+// 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.Linq;
+using System.Collections.Immutable;
+using System.Diagnostics;
+
+namespace Microsoft.CodeAnalysis
+{
+ internal abstract class CommonSyntaxAndDeclarationManager
+ {
+ internal readonly ImmutableArray ExternalSyntaxTrees;
+ internal readonly string ScriptClassName;
+ internal readonly SourceReferenceResolver Resolver;
+ internal readonly CommonMessageProvider MessageProvider;
+ internal readonly bool IsSubmission;
+
+ private ImmutableDictionary> _syntaxTreeLoadDirectiveMap;
+ // This ImmutableDictionary will use default (case-sensitive) comparison
+ // for its keys. It is the responsibility of the SourceReferenceResolver
+ // to normalize the paths it resolves in a way that is appropriate for the
+ // platforms that the host supports.
+ private ImmutableDictionary _resolvedFilePathSyntaxTreeMap;
+
+ public CommonSyntaxAndDeclarationManager(
+ ImmutableArray externalSyntaxTrees,
+ string scriptClassName,
+ SourceReferenceResolver resolver,
+ CommonMessageProvider messageProvider,
+ bool isSubmission)
+ {
+ this.ExternalSyntaxTrees = externalSyntaxTrees;
+ this.ScriptClassName = scriptClassName ?? "";
+ this.Resolver = resolver; // TODO: What if SourceReferenceResolver is null?
+ this.MessageProvider = messageProvider;
+ this.IsSubmission = isSubmission;
+ }
+ }
+}
diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs
index 69cc6b234cd864483b451ea6b099251e718c0d4d..78b504f2def0bdc80196c0a5e2ca0ca6a2a4ed1d 100644
--- a/src/Compilers/Core/Portable/Compilation/Compilation.cs
+++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs
@@ -32,9 +32,6 @@ namespace Microsoft.CodeAnalysis
///
public abstract partial class Compilation
{
- // Inverse of syntaxTrees array (i.e. maps tree to index)
- internal readonly ImmutableDictionary syntaxTreeOrdinalMap;
-
///
/// 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
@@ -58,17 +55,17 @@ public abstract partial class Compilation
internal Compilation(
string name,
ImmutableArray references,
+ IReadOnlyDictionary features,
Type submissionReturnType,
Type hostObjectType,
bool isSubmission,
- ImmutableDictionary syntaxTreeOrdinalMap,
AsyncQueue eventQueue)
{
Debug.Assert(!references.IsDefault);
+ Debug.Assert(features != null);
this.AssemblyName = name;
this.ExternalReferences = references;
- this.syntaxTreeOrdinalMap = syntaxTreeOrdinalMap;
this.EventQueue = eventQueue;
if (isSubmission)
@@ -82,10 +79,10 @@ public abstract partial class Compilation
_lazySubmissionSlotIndex = SubmissionSlotIndexNotApplicable;
}
- _features = SyntaxTreeCommonFeatures(syntaxTreeOrdinalMap.Keys);
+ _features = features;
}
- private IReadOnlyDictionary SyntaxTreeCommonFeatures(IEnumerable trees)
+ protected static IReadOnlyDictionary SyntaxTreeCommonFeatures(IEnumerable trees)
{
IReadOnlyDictionary set = null;
@@ -2137,14 +2134,10 @@ internal int CompareSyntaxTreeOrdering(SyntaxTree tree1, SyntaxTree tree2)
Debug.Assert(this.ContainsSyntaxTree(tree1));
Debug.Assert(this.ContainsSyntaxTree(tree2));
- return this.syntaxTreeOrdinalMap[tree1] - this.syntaxTreeOrdinalMap[tree2];
+ return this.GetSyntaxTreeOrdinal(tree1) - this.GetSyntaxTreeOrdinal(tree2);
}
- internal int GetSyntaxTreeOrdinal(SyntaxTree tree)
- {
- Debug.Assert(this.ContainsSyntaxTree(tree));
- return this.syntaxTreeOrdinalMap[tree];
- }
+ internal abstract int GetSyntaxTreeOrdinal(SyntaxTree tree);
///
/// Compare two source locations, using their containing trees, and then by Span.First within a tree.
diff --git a/src/Compilers/Core/Portable/Compilation/LoadDirective.cs b/src/Compilers/Core/Portable/Compilation/LoadDirective.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2ca5ee522be1f3552a1c604d9d97defc036af6d0
--- /dev/null
+++ b/src/Compilers/Core/Portable/Compilation/LoadDirective.cs
@@ -0,0 +1,42 @@
+// 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.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis
+{
+ internal struct LoadDirective : IEquatable
+ {
+ public readonly string ResolvedPath;
+ public readonly ImmutableArray Diagnostics;
+
+ public LoadDirective(string resolvedPath, ImmutableArray diagnostics)
+ {
+ Debug.Assert((resolvedPath != null) || !diagnostics.IsEmpty);
+ Debug.Assert(!diagnostics.IsDefault);
+ Debug.Assert(diagnostics.IsEmpty || diagnostics.All(d => d.Severity == DiagnosticSeverity.Error));
+
+ ResolvedPath = resolvedPath;
+ Diagnostics = diagnostics;
+ }
+
+ public bool Equals(LoadDirective other)
+ {
+ return this.ResolvedPath == other.ResolvedPath &&
+ this.Diagnostics.SequenceEqual(other.Diagnostics);
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is LoadDirective && Equals((LoadDirective)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return Hash.Combine(this.Diagnostics.GetHashCode(), this.ResolvedPath.GetHashCode());
+ }
+ }
+}
diff --git a/src/Compilers/Core/Portable/Compilation/SourceReferenceResolver.cs b/src/Compilers/Core/Portable/Compilation/SourceReferenceResolver.cs
index 09f6231e0eca74d4823f6c2962b03873f375fcac..9b0901954c70406160766dc586f0004a265f1e3e 100644
--- a/src/Compilers/Core/Portable/Compilation/SourceReferenceResolver.cs
+++ b/src/Compilers/Core/Portable/Compilation/SourceReferenceResolver.cs
@@ -1,7 +1,9 @@
// 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.Generic;
using System.IO;
+using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis
{
@@ -53,5 +55,14 @@ internal Stream OpenReadChecked(string fullPath)
return stream;
}
+
+ ///
+ /// Reads the contents of and returns a .
+ ///
+ /// Path returned by .
+ public virtual SourceText ReadText(string resolvedPath)
+ {
+ return SourceText.From(OpenRead(resolvedPath));
+ }
}
}
diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt
index fbd531443c94890a552b4cd5e19b2ef0b908ce8a..225f240752827bd02b117e999e54c31cfe369ae0 100644
--- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt
+++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt
@@ -48,4 +48,5 @@ static Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzerExtensions.WithAnaly
static Microsoft.CodeAnalysis.Diagnostics.Telemetry.AnalyzerTelemetry.GetAnalyzerActionCountsAsync(this Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers compilationWithAnalyzers, Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer analyzer, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
static Microsoft.CodeAnalysis.Diagnostics.Telemetry.AnalyzerTelemetry.GetAnalyzerExecutionTime(this Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers compilationWithAnalyzers, Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer analyzer) -> System.TimeSpan
static Microsoft.CodeAnalysis.SyntaxNodeExtensions.NormalizeWhitespace(this TNode node, string indentation = " ", string eol = "\r\n", bool elasticTrivia = false) -> TNode
-static Microsoft.CodeAnalysis.SyntaxNodeExtensions.NormalizeWhitespace(this TNode node, string indentation, bool elasticTrivia) -> TNode
\ No newline at end of file
+static Microsoft.CodeAnalysis.SyntaxNodeExtensions.NormalizeWhitespace(this TNode node, string indentation, bool elasticTrivia) -> TNode
+virtual Microsoft.CodeAnalysis.SourceReferenceResolver.ReadText(string resolvedPath) -> Microsoft.CodeAnalysis.Text.SourceText
\ No newline at end of file
diff --git a/src/Compilers/Core/SharedCollections/PooledDictionary.cs b/src/Compilers/Core/SharedCollections/PooledDictionary.cs
index e09b6a5b9c2bc457eb04be81571f482abf5feca2..bd2185b90582c48053b088af8fd780e257f01821 100644
--- a/src/Compilers/Core/SharedCollections/PooledDictionary.cs
+++ b/src/Compilers/Core/SharedCollections/PooledDictionary.cs
@@ -1,6 +1,7 @@
// 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.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
using Roslyn.Utilities;
@@ -17,6 +18,13 @@ private PooledDictionary(ObjectPool> pool)
_pool = pool;
}
+ public ImmutableDictionary ToImmutableDictionaryAndFree()
+ {
+ var result = this.ToImmutableDictionary();
+ this.Free();
+ return result;
+ }
+
public void Free()
{
this.Clear();
diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb
index 01ff85f245f371395c68d1366df4f203aaf4d879..158c6aad8c27e83c21a2b37be762533061a78652 100644
--- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb
+++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb
@@ -80,6 +80,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
'''
Private ReadOnly _syntaxTrees As ImmutableArray(Of SyntaxTree)
+ Private ReadOnly _syntaxTreeOrdinalMap As ImmutableDictionary(Of SyntaxTree, Integer)
+
'''
''' The syntax trees of this compilation plus all 'hidden' trees
''' added to the compilation by compiler, e.g. Vb Core Runtime.
@@ -411,7 +413,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
reuseReferenceManager As Boolean,
Optional eventQueue As AsyncQueue(Of CompilationEvent) = Nothing
)
- MyBase.New(assemblyName, references, submissionReturnType, hostObjectType, isSubmission, syntaxTreeOrdinalMap, eventQueue)
+ MyBase.New(assemblyName, references, SyntaxTreeCommonFeatures(syntaxTrees), submissionReturnType, hostObjectType, isSubmission, eventQueue)
Debug.Assert(rootNamespaces IsNot Nothing)
Debug.Assert(declarationTable IsNot Nothing)
@@ -421,6 +423,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
_options = options
_syntaxTrees = syntaxTrees
+ _syntaxTreeOrdinalMap = syntaxTreeOrdinalMap
_rootNamespaces = rootNamespaces
_embeddedTrees = embeddedTrees
_declarationTable = declarationTable
@@ -486,7 +489,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
_options,
Me.ExternalReferences,
_syntaxTrees,
- Me.syntaxTreeOrdinalMap,
+ _syntaxTreeOrdinalMap,
_rootNamespaces,
_embeddedTrees,
_declarationTable,
@@ -538,7 +541,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Me.Options,
Me.ExternalReferences,
_syntaxTrees,
- Me.syntaxTreeOrdinalMap,
+ _syntaxTreeOrdinalMap,
_rootNamespaces,
_embeddedTrees,
_declarationTable,
@@ -579,7 +582,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Me.Options,
ValidateReferences(Of VisualBasicCompilationReference)(newReferences),
_syntaxTrees,
- Me.syntaxTreeOrdinalMap,
+ _syntaxTreeOrdinalMap,
_rootNamespaces,
embeddedTrees,
declTable,
@@ -632,7 +635,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
newOptions,
Me.ExternalReferences,
_syntaxTrees,
- Me.syntaxTreeOrdinalMap,
+ _syntaxTreeOrdinalMap,
declMap,
embeddedTrees,
declTable,
@@ -660,7 +663,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Me.Options,
Me.ExternalReferences,
_syntaxTrees,
- Me.syntaxTreeOrdinalMap,
+ _syntaxTreeOrdinalMap,
_rootNamespaces,
_embeddedTrees,
_declarationTable,
@@ -681,7 +684,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Me.Options,
Me.ExternalReferences,
_syntaxTrees,
- Me.syntaxTreeOrdinalMap,
+ _syntaxTreeOrdinalMap,
_rootNamespaces,
_embeddedTrees,
_declarationTable,
@@ -845,7 +848,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Dim referenceDirectivesChanged = False
Dim oldTreeCount = _syntaxTrees.Length
- Dim ordinalMap = Me.syntaxTreeOrdinalMap
+ Dim ordinalMap = _syntaxTreeOrdinalMap
Dim declMap = _rootNamespaces
Dim declTable = _declarationTable
Dim i = 0
@@ -1020,7 +1023,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
RemoveSyntaxTreeFromDeclarationMapAndTable(vbOldTree, declMap, declTable, referenceDirectivesChanged)
AddSyntaxTreeToDeclarationMapAndTable(vbNewTree, _options, Me.IsSubmission, declMap, declTable, referenceDirectivesChanged)
- Dim ordinalMap = Me.syntaxTreeOrdinalMap
+ Dim ordinalMap = _syntaxTreeOrdinalMap
Debug.Assert(ordinalMap.ContainsKey(oldTree)) ' Checked by RemoveSyntaxTreeFromDeclarationMapAndTable
Dim oldOrdinal = ordinalMap(oldTree)
@@ -1159,6 +1162,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return LexicalSortKey.Compare(first, second, Me)
End Function
+ Friend Overrides Function GetSyntaxTreeOrdinal(tree As SyntaxTree) As Integer
+ Debug.Assert(Me.ContainsSyntaxTree(tree))
+ Return _syntaxTreeOrdinalMap(tree)
+ End Function
+
#End Region
#Region "References"
diff --git a/src/Interactive/HostTest/InteractiveHostTests.cs b/src/Interactive/HostTest/InteractiveHostTests.cs
index 8baf863a955d1ae7a17379d4d7e41f9a39a9efab..ea01dcbb5b612d5eb254e7a015773d3f0442706a 100644
--- a/src/Interactive/HostTest/InteractiveHostTests.cs
+++ b/src/Interactive/HostTest/InteractiveHostTests.cs
@@ -420,8 +420,8 @@ public void AsyncExecuteFile_ScriptFileWithBuildErrors()
Host.ExecuteFileAsync(file.Path).Wait();
var errorOut = ReadErrorOutputToEnd().Trim();
- Assert.True(errorOut.StartsWith(file.Path + "(1,2):", StringComparison.Ordinal), "Error output should start with file name, line and column");
- Assert.True(errorOut.Contains("CS1024"), "Error output should include error CS1024");
+ Assert.True(errorOut.StartsWith(file.Path + "(1,7):", StringComparison.Ordinal), "Error output should start with file name, line and column");
+ Assert.True(errorOut.Contains("CS7010"), "Error output should include error CS7010");
}
///
diff --git a/src/Scripting/CSharp/CSharpScriptCompiler.cs b/src/Scripting/CSharp/CSharpScriptCompiler.cs
index 65528d6c32d3630582f56426c781d8ebaf95c5e4..9bd9705aa1e6ac811c7970a145ee32b6885e77d1 100644
--- a/src/Scripting/CSharp/CSharpScriptCompiler.cs
+++ b/src/Scripting/CSharp/CSharpScriptCompiler.cs
@@ -1,8 +1,10 @@
// 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.Immutable;
using System.Globalization;
using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Scripting.CSharp
{
@@ -44,12 +46,12 @@ public override Compilation CreateSubmission(Script script)
scriptClassName: submissionTypeName,
usings: script.Options.Namespaces,
optimizationLevel: OptimizationLevel.Debug, // TODO
- checkOverflow: false, // TODO
- allowUnsafe: true, // TODO
+ checkOverflow: false, // TODO
+ allowUnsafe: true, // TODO
platform: Platform.AnyCpu,
warningLevel: 4,
xmlReferenceResolver: null, // don't support XML file references in interactive (permissions & doc comment includes)
- sourceReferenceResolver: SourceFileResolver.Default, // TODO
+ sourceReferenceResolver: LoadDirectiveResolver.Default,
metadataReferenceResolver: script.Options.ReferenceResolver,
assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default
),
@@ -60,5 +62,25 @@ public override Compilation CreateSubmission(Script script)
return compilation;
}
+
+ private class LoadDirectiveResolver : SourceFileResolver
+ {
+ public static new LoadDirectiveResolver Default { get; } = new LoadDirectiveResolver();
+
+ private LoadDirectiveResolver()
+ : base(ImmutableArray.Empty, baseDirectory: null)
+ {
+ }
+
+ public override SourceText ReadText(string resolvedPath)
+ {
+ string unused;
+ return CommonCompiler.ReadFileContentHelper(
+ resolvedPath,
+ encoding: null,
+ checksumAlgorithm: SourceHashAlgorithm.Sha1, // TODO: Should we be fetching the checksum algorithm from somewhere?
+ normalizedFilePath: out unused);
+ }
+ }
}
}
diff --git a/src/Scripting/CSharp/CSharpScripting.csproj b/src/Scripting/CSharp/CSharpScripting.csproj
index 2dc4584bbd2b1250335d0e0dd0b9e355ae54a489..62d728a3cabc7a99b60ca4fdf2336220ca413ec2 100644
--- a/src/Scripting/CSharp/CSharpScripting.csproj
+++ b/src/Scripting/CSharp/CSharpScripting.csproj
@@ -54,11 +54,6 @@
-
- True
- True
- CSharpScriptingResources.resx
-
@@ -67,12 +62,6 @@
-
-
- ResXFileCodeGenerator
- CSharpScriptingResources.Designer.cs
-
-
diff --git a/src/Scripting/CSharp/CSharpScriptingResources.Designer.cs b/src/Scripting/CSharp/CSharpScriptingResources.Designer.cs
deleted file mode 100644
index a639f6cf194b2c35ec51777f89b7fdd6adeb741c..0000000000000000000000000000000000000000
--- a/src/Scripting/CSharp/CSharpScriptingResources.Designer.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace Microsoft.CodeAnalysis.Scripting.CSharp {
- using System;
- using System.Reflection;
-
-
- ///
- /// A strongly-typed resource class, for looking up localized strings, etc.
- ///
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class CSharpScriptingResources {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal CSharpScriptingResources() {
- }
-
- ///
- /// Returns the cached ResourceManager instance used by this class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CodeAnalysis.Scripting.CSharp.CSharpScriptingResources", typeof(CSharpScriptingResources).GetTypeInfo().Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- ///
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
- }
-}
diff --git a/src/Scripting/CSharp/CSharpScriptingResources.resx b/src/Scripting/CSharp/CSharpScriptingResources.resx
deleted file mode 100644
index 4fdb1b6aff69ba96d81420fab7a92b738c17f074..0000000000000000000000000000000000000000
--- a/src/Scripting/CSharp/CSharpScriptingResources.resx
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 1.3
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/src/Scripting/Core/Script.cs b/src/Scripting/Core/Script.cs
index 6444c75f77515111dcfa428a75375f160b7b94b1..43e96acf8ae962195ace9381ff324bbc0e486345 100644
--- a/src/Scripting/Core/Script.cs
+++ b/src/Scripting/Core/Script.cs
@@ -24,7 +24,7 @@ public abstract class Script
private ScriptBuilder _lazyBuilder;
private Compilation _lazyCompilation;
-
+
internal Script(ScriptCompiler compiler, string code, ScriptOptions options, Type globalsType, ScriptBuilder builder, Script previous)
{
Compiler = compiler;
@@ -116,7 +116,7 @@ internal ScriptBuilder Builder
/// The type that defines members that can be accessed by the script.
public Script WithGlobalsType(Type globalsType) => this.WithGlobalsTypeInternal(globalsType);
internal abstract Script WithGlobalsTypeInternal(Type globalsType);
-
+
///
/// Continues the script with given code snippet.
///
@@ -215,14 +215,14 @@ internal ImmutableArray GetReferencesForCompilation()
references = references.Add(corLib);
if (this.GlobalsType != null)
- {
+ {
var globalsTypeAssembly = MetadataReference.CreateFromAssemblyInternal(this.GlobalsType.GetTypeInfo().Assembly);
references = references.Add(globalsTypeAssembly);
}
return references;
- }
- }
+ }
+ }
public sealed class Script : Script
{
@@ -248,19 +248,19 @@ public new Script WithCode(string code)
if (code == null)
{
code = "";
- }
+ }
return (code == this.Code) ?
this :
new Script(this.Compiler, code, this.Options, this.GlobalsType, this.LazyBuilder, this.Previous);
- }
+ }
public new Script WithGlobalsType(Type globalsType)
- {
+ {
return (globalsType == this.GlobalsType) ?
this :
new Script(this.Compiler, this.Code, this.Options, globalsType, this.LazyBuilder, this.Previous);
- }
+ }
internal override Script WithOptionsInternal(ScriptOptions options) => WithOptions(options);
internal override Script WithCodeInternal(string code) => WithCode(code);
@@ -268,10 +268,10 @@ public new Script WithGlobalsType(Type globalsType)
/// Compilation has errors.
internal override void CommonBuild(CancellationToken cancellationToken)
- {
+ {
GetPrecedingExecutors(cancellationToken);
GetExecutor(cancellationToken);
- }
+ }
internal override Func