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 CommonGetExecutor(CancellationToken cancellationToken) => GetExecutor(cancellationToken); @@ -304,7 +304,7 @@ internal override void CommonBuild(CancellationToken cancellationToken) var preceding = TryGetPrecedingExecutors(null, cancellationToken); Debug.Assert(!preceding.IsDefault); InterlockedOperations.Initialize(ref _lazyPrecedingExecutors, preceding); - } + } return _lazyPrecedingExecutors; } @@ -314,32 +314,32 @@ internal override void CommonBuild(CancellationToken cancellationToken) { Script script = Previous; if (script == lastExecutedScriptInChainOpt) - { + { return ImmutableArray>.Empty; - } + } var scriptsReversed = ArrayBuilder