提交 05b2ed1b 编写于 作者: C Cyrus Najmabadi

Merge remote-tracking branch 'upstream/master' into allowSymbolOOPAndNoSymAndProjId

## API Review Notes for September 30th, 2019
### Changes reviewed
Starting commit: `38c90f8401f9e3ee5fb7c82aac36f6b85fdda979`
Ending Commit: `b8a5611e3db4f7ac3b5e1404b129304b5baf843e`
### Notes
IVariableDeclarationOperation: IVariableDeclarationOperation:
- What does scripting do for using local declarations in IOperation? - What does scripting do for using local declarations in IOperation?
......
## API Review Notes for April 15th, 2020
### Changes reviewed
Starting commit: `c827247a767c0c434dcb77496038b163b9e011da`
Ending Commit: `3375a7b209d1f590940af043e9e39f2cbb503845`
### Notes
#### Everything but Source Generators:
* `Project.RemoveDocuments`: Everything else in this type uses `IEnumerable<T>`, not `ImmutableArray<T>`. If we could start again from day 1 we might make everything that, but given that everything today uses the former, we should do so here. This applies to the overloads on `Solution` as well.
* `Solution.RemoveAdditonalDocuments/RemoveAnalyzerConfigDocuments`: We added these to `Solution`, but not to project, even though that also has the invididual `Remove` methods. We should add them to `Project` as well.
* `DocumentOptionSet.GetOption`: This API was added as a `new` on the concrete `DocumentOptionSet` to cover a change when we moved the implementation up to `OptionSet` and made the method non-virtual. This is not actually a breaking change, so we should remove this.
#### Source Generators:
As a general note, changes that we make here are _not_ considered breaking changes post 16.6.
However, we did take a broad overview of the API surface and have some notes for @chsienki.
* `GeneratorAttribute`: we should consider adding a `LanguageName` attribute to this type.
* `InitializationContext`: Rename to something like `SourceGeneratorInitialzationContext` or similar. Current name is ambiguous.
* `InitializationContext.RegisterForNotifications`:
* Naming is inconsistent with existing `Action` ending for analyzer register methods.
* `SyntaxReceiverCreator`: all other `RegisterForXAction` methods in analyzers use an `Action` or `Func`, as appropriate. We should consider doing the same here.
* `ISyntaxReceiver`: This may be redundant with existing APIs.
* `SourceGeneratorContext`: There is not currently a way to share semantic models and other binding info between source generators. We should design a way for this info to be shared so we can avoid introducing lots of binding from generators.
ErrorLogPath removed from CLO, moved to ErrorLogOptions
- We should probably maintain this API
- Just forward to the old implementation
TextDocument
- Should we not remove the constructor? This is a binary breaking change
- This empty constructor cannot be used.
- We'll keep the change.
Workspace
- CanApplyParseOptionChange going from virtual protected to virtual public is a source and binary breaking change
- We should look at having a new non-virtual public method on Workspace and forward to this API.
SyntaxFactory.AnonymousMethodExpression
- Added overload with multiple parameters
- Followup on usage
Formatter.OrganizeUsings
- Should make cancellationtoken optional?
- Need to followup with framework on what the current guidelines are
- Make this default for now
...@@ -35,9 +35,9 @@ internal CSharpCommandLineParser(bool isScriptCommandLineParser = false) ...@@ -35,9 +35,9 @@ internal CSharpCommandLineParser(bool isScriptCommandLineParser = false)
protected override string RegularFileExtension { get { return ".cs"; } } protected override string RegularFileExtension { get { return ".cs"; } }
protected override string ScriptFileExtension { get { return ".csx"; } } protected override string ScriptFileExtension { get { return ".csx"; } }
internal sealed override CommandLineArguments CommonParse(IEnumerable<string> args, string baseDirectory, string? sdkDirectoryOpt, string additionalReferenceDirectories) internal sealed override CommandLineArguments CommonParse(IEnumerable<string> args, string baseDirectory, string? sdkDirectory, string? additionalReferenceDirectories)
{ {
return Parse(args, baseDirectory, sdkDirectoryOpt, additionalReferenceDirectories); return Parse(args, baseDirectory, sdkDirectory, additionalReferenceDirectories);
} }
/// <summary> /// <summary>
......
...@@ -67,7 +67,7 @@ internal virtual IEnumerable<string> EnumerateFiles(string? directory, string fi ...@@ -67,7 +67,7 @@ internal virtual IEnumerable<string> EnumerateFiles(string? directory, string fi
return Directory.EnumerateFiles(directory, fileNamePattern, searchOption); return Directory.EnumerateFiles(directory, fileNamePattern, searchOption);
} }
internal abstract CommandLineArguments CommonParse(IEnumerable<string> args, string baseDirectory, string? sdkDirectoryOpt, string additionalReferenceDirectories); internal abstract CommandLineArguments CommonParse(IEnumerable<string> args, string baseDirectory, string? sdkDirectory, string? additionalReferenceDirectories);
/// <summary> /// <summary>
/// Parses a command line. /// Parses a command line.
...@@ -77,7 +77,7 @@ internal virtual IEnumerable<string> EnumerateFiles(string? directory, string fi ...@@ -77,7 +77,7 @@ internal virtual IEnumerable<string> EnumerateFiles(string? directory, string fi
/// <param name="sdkDirectory">The directory to search for mscorlib, or null if not available.</param> /// <param name="sdkDirectory">The directory to search for mscorlib, or null if not available.</param>
/// <param name="additionalReferenceDirectories">A string representing additional reference paths.</param> /// <param name="additionalReferenceDirectories">A string representing additional reference paths.</param>
/// <returns>a <see cref="CommandLineArguments"/> object representing the parsed command line.</returns> /// <returns>a <see cref="CommandLineArguments"/> object representing the parsed command line.</returns>
public CommandLineArguments Parse(IEnumerable<string> args, string baseDirectory, string? sdkDirectory, string additionalReferenceDirectories) public CommandLineArguments Parse(IEnumerable<string> args, string baseDirectory, string? sdkDirectory, string? additionalReferenceDirectories)
{ {
return CommonParse(args, baseDirectory, sdkDirectory, additionalReferenceDirectories); return CommonParse(args, baseDirectory, sdkDirectory, additionalReferenceDirectories);
} }
......
...@@ -101,9 +101,8 @@ private async Task<DefinitionItem> GetDefinitionItemAsync(ISymbol definition) ...@@ -101,9 +101,8 @@ private async Task<DefinitionItem> GetDefinitionItemAsync(ISymbol definition)
{ {
if (!_definitionToItem.TryGetValue(definition, out var definitionItem)) if (!_definitionToItem.TryGetValue(definition, out var definitionItem))
{ {
var projectId = _solution.GetExactProjectId(definition);
definitionItem = await definition.ToClassifiedDefinitionItemAsync( definitionItem = await definition.ToClassifiedDefinitionItemAsync(
_solution.GetProject(projectId), includeHiddenLocations: false, _solution.GetOriginatingProject(definition), includeHiddenLocations: false,
_options, _context.CancellationToken).ConfigureAwait(false); _options, _context.CancellationToken).ConfigureAwait(false);
_definitionToItem[definition] = definitionItem; _definitionToItem[definition] = definitionItem;
......
...@@ -61,18 +61,14 @@ public static async Task<(Solution solution, ISymbol symbol, ImmutableArray<ISym ...@@ -61,18 +61,14 @@ public static async Task<(Solution solution, ISymbol symbol, ImmutableArray<ISym
var (symbol, project) = symbolAndProjectOpt.Value; var (symbol, project) = symbolAndProjectOpt.Value;
return await FindSourceImplementationsAsync( return await FindSourceImplementationsAsync(
symbol, project.Solution, cancellationToken).ConfigureAwait(false); project.Solution, symbol, cancellationToken).ConfigureAwait(false);
} }
private static async Task<(Solution solution, ISymbol symbol, ImmutableArray<ISymbol> implementations, string message)?> FindSourceImplementationsAsync( private static async Task<(Solution solution, ISymbol symbol, ImmutableArray<ISymbol> implementations, string message)?> FindSourceImplementationsAsync(
ISymbol symbol, Solution solution, CancellationToken cancellationToken) Solution solution, ISymbol symbol, CancellationToken cancellationToken)
{ {
var builder = new HashSet<ISymbol>(SymbolEquivalenceComparer.Instance); var builder = new HashSet<ISymbol>(SymbolEquivalenceComparer.Instance);
// Find the direct implementations first.
builder.AddRange(await FindSourceImplementationsWorkerAsync(
symbol, solution, cancellationToken).ConfigureAwait(false));
// If we're in a linked file, try to find all the symbols this links to, and find all the implementations of // If we're in a linked file, try to find all the symbols this links to, and find all the implementations of
// each of those linked symbols. De-dupe the results so the user only gets unique results. // each of those linked symbols. De-dupe the results so the user only gets unique results.
var linkedSymbols = await SymbolFinder.FindLinkedSymbolsAsync( var linkedSymbols = await SymbolFinder.FindLinkedSymbolsAsync(
...@@ -81,25 +77,24 @@ public static async Task<(Solution solution, ISymbol symbol, ImmutableArray<ISym ...@@ -81,25 +77,24 @@ public static async Task<(Solution solution, ISymbol symbol, ImmutableArray<ISym
foreach (var linkedSymbol in linkedSymbols) foreach (var linkedSymbol in linkedSymbols)
{ {
builder.AddRange(await FindSourceImplementationsWorkerAsync( builder.AddRange(await FindSourceImplementationsWorkerAsync(
linkedSymbol, solution, cancellationToken).ConfigureAwait((bool)false)); solution, linkedSymbol, cancellationToken).ConfigureAwait((bool)false));
} }
var result = builder.ToImmutableArray(); var result = builder.ToImmutableArray();
var message = result.IsEmpty ? EditorFeaturesResources.The_symbol_has_no_implementations : null;
return result.Length == 0 return (solution, symbol, result, message);
? (solution, symbol, result, EditorFeaturesResources.The_symbol_has_no_implementations)
: (solution, symbol, result, null);
} }
private static async Task<ImmutableArray<ISymbol>> FindSourceImplementationsWorkerAsync( private static async Task<ImmutableArray<ISymbol>> FindSourceImplementationsWorkerAsync(
ISymbol symbol, Solution solution, CancellationToken cancellationToken) Solution solution, ISymbol symbol, CancellationToken cancellationToken)
{ {
var implementations = await FindSourceAndMetadataImplementationsAsync(symbol, solution, cancellationToken).ConfigureAwait(false); var implementations = await FindSourceAndMetadataImplementationsAsync(solution, symbol, cancellationToken).ConfigureAwait(false);
return implementations.WhereAsArray(s => s.Locations.Any(l => l.IsInSource)); return implementations.WhereAsArray(s => s.Locations.Any(l => l.IsInSource));
} }
private static async Task<ImmutableArray<ISymbol>> FindSourceAndMetadataImplementationsAsync( private static async Task<ImmutableArray<ISymbol>> FindSourceAndMetadataImplementationsAsync(
ISymbol symbol, Solution solution, CancellationToken cancellationToken) Solution solution, ISymbol symbol, CancellationToken cancellationToken)
{ {
if (symbol.IsInterfaceType() || symbol.IsImplementableMember()) if (symbol.IsInterfaceType() || symbol.IsImplementableMember())
{ {
......
...@@ -47,11 +47,10 @@ public async Task FindBasesAsync(Document document, int position, IFindUsagesCon ...@@ -47,11 +47,10 @@ public async Task FindBasesAsync(Document document, int position, IFindUsagesCon
baseSymbol, solution, cancellationToken).ConfigureAwait(false); baseSymbol, solution, cancellationToken).ConfigureAwait(false);
if (sourceDefinition != null) if (sourceDefinition != null)
{ {
var sourceDefProjectId = solution.GetExactProjectId(sourceDefinition);
var definitionItem = await sourceDefinition.ToClassifiedDefinitionItemAsync( var definitionItem = await sourceDefinition.ToClassifiedDefinitionItemAsync(
solution.GetProject(sourceDefProjectId), includeHiddenLocations: false, solution.GetOriginatingProject(sourceDefinition), includeHiddenLocations: false,
FindReferencesSearchOptions.Default, cancellationToken: cancellationToken) FindReferencesSearchOptions.Default, cancellationToken: cancellationToken).ConfigureAwait(false);
.ConfigureAwait(false);
await context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false); await context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false);
found = true; found = true;
} }
......
...@@ -2,18 +2,20 @@ ...@@ -2,18 +2,20 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
namespace Microsoft.CodeAnalysis.Interactive namespace Microsoft.CodeAnalysis.Interactive
{ {
internal partial class InteractiveHost internal partial class InteractiveHost
{ {
private struct InitializedRemoteService private readonly struct InitializedRemoteService
{ {
public readonly RemoteService ServiceOpt; public readonly RemoteService Service;
public readonly RemoteExecutionResult InitializationResult; public readonly RemoteExecutionResult InitializationResult;
public InitializedRemoteService(RemoteService service, RemoteExecutionResult initializationResult) public InitializedRemoteService(RemoteService service, RemoteExecutionResult initializationResult)
{ {
ServiceOpt = service; Service = service;
InitializationResult = initializationResult; InitializationResult = initializationResult;
} }
} }
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
using System; using System;
using System.Globalization; using System.Globalization;
using System.Threading; using System.Threading;
...@@ -41,7 +43,7 @@ public void Dispose() ...@@ -41,7 +43,7 @@ public void Dispose()
// If the value has been calculated already, dispose the service. // If the value has been calculated already, dispose the service.
if (InitializedService.TryGetValue(out var initializedService)) if (InitializedService.TryGetValue(out var initializedService))
{ {
initializedService.ServiceOpt?.Dispose(); initializedService.Service?.Dispose();
} }
} }
...@@ -101,7 +103,7 @@ private async Task<InitializedRemoteService> TryStartAndInitializeProcessAsync(C ...@@ -101,7 +103,7 @@ private async Task<InitializedRemoteService> TryStartAndInitializeProcessAsync(C
} }
} }
private Task<RemoteService> TryStartProcessAsync(string hostPath, CultureInfo culture, CancellationToken cancellationToken) private Task<RemoteService?> TryStartProcessAsync(string hostPath, CultureInfo culture, CancellationToken cancellationToken)
{ {
return Task.Run(() => Host.TryStartProcess(hostPath, culture, cancellationToken)); return Task.Run(() => Host.TryStartProcess(hostPath, culture, cancellationToken));
} }
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.Remoting; using System.Runtime.Remoting;
...@@ -12,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Interactive ...@@ -12,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Interactive
{ {
internal partial class InteractiveHost internal partial class InteractiveHost
{ {
internal class RemoteAsyncOperation<TResult> : MarshalByRefObject internal sealed class RemoteAsyncOperation<TResult> : MarshalByRefObject
{ {
private readonly RemoteService _remoteService; private readonly RemoteService _remoteService;
private readonly TaskCompletionSource<TResult> _completion; private readonly TaskCompletionSource<TResult> _completion;
...@@ -20,17 +22,12 @@ internal class RemoteAsyncOperation<TResult> : MarshalByRefObject ...@@ -20,17 +22,12 @@ internal class RemoteAsyncOperation<TResult> : MarshalByRefObject
internal RemoteAsyncOperation(RemoteService service) internal RemoteAsyncOperation(RemoteService service)
{ {
Debug.Assert(service != null);
_remoteService = service; _remoteService = service;
_completion = new TaskCompletionSource<TResult>(); _completion = new TaskCompletionSource<TResult>();
_processExitedHandler = new EventHandler((_, __) => ProcessExited()); _processExitedHandler = new EventHandler((_, __) => ProcessExited());
} }
public override object InitializeLifetimeService() public override object? InitializeLifetimeService() => null;
{
return null;
}
public Task<TResult> ExecuteAsync(Action<Service, RemoteAsyncOperation<TResult>> action) public Task<TResult> ExecuteAsync(Action<Service, RemoteAsyncOperation<TResult>> action)
{ {
...@@ -67,7 +64,7 @@ public void Completed(TResult result) ...@@ -67,7 +64,7 @@ public void Completed(TResult result)
private void ProcessExited() private void ProcessExited()
{ {
Completed(result: default(TResult)); Completed(result: default!);
} }
public void SetResult(TResult result) public void SetResult(TResult result)
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
...@@ -23,17 +25,13 @@ internal sealed class RemoteService ...@@ -23,17 +25,13 @@ internal sealed class RemoteService
private readonly bool _joinOutputWritingThreadsOnDisposal; private readonly bool _joinOutputWritingThreadsOnDisposal;
// output pumping threads (stream output from stdout/stderr of the host process to the output/errorOutput writers) // output pumping threads (stream output from stdout/stderr of the host process to the output/errorOutput writers)
private InteractiveHost _host; // nulled on dispose private InteractiveHost? _host; // nulled on dispose
private Thread _readOutputThread; // nulled on dispose private Thread? _readOutputThread; // nulled on dispose
private Thread _readErrorOutputThread; // nulled on dispose private Thread? _readErrorOutputThread; // nulled on dispose
private volatile ProcessExitHandlerStatus _processExitHandlerStatus; // set to Handled on dispose private volatile ProcessExitHandlerStatus _processExitHandlerStatus; // set to Handled on dispose
internal RemoteService(InteractiveHost host, Process process, int processId, Service service) internal RemoteService(InteractiveHost host, Process process, int processId, Service service)
{ {
Debug.Assert(host != null);
Debug.Assert(process != null);
Debug.Assert(service != null);
Process = process; Process = process;
Service = service; Service = service;
......
// Licensed to the .NET Foundation under one or more agreements. // Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
extern alias Scripting; extern alias Scripting;
using System; using System;
...@@ -10,7 +13,6 @@ ...@@ -10,7 +13,6 @@
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Remoting; using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels;
...@@ -27,8 +29,6 @@ ...@@ -27,8 +29,6 @@
namespace Microsoft.CodeAnalysis.Interactive namespace Microsoft.CodeAnalysis.Interactive
{ {
using RelativePathResolver = Scripting::Microsoft.CodeAnalysis.RelativePathResolver;
internal partial class InteractiveHost internal partial class InteractiveHost
{ {
/// <summary> /// <summary>
...@@ -38,12 +38,9 @@ internal sealed class Service : MarshalByRefObject, IDisposable ...@@ -38,12 +38,9 @@ internal sealed class Service : MarshalByRefObject, IDisposable
{ {
private static readonly ManualResetEventSlim s_clientExited = new ManualResetEventSlim(false); private static readonly ManualResetEventSlim s_clientExited = new ManualResetEventSlim(false);
private static Control s_control; private static Control? s_control;
private InteractiveAssemblyLoader _assemblyLoader; private ServiceState? _serviceState;
private MetadataShadowCopyProvider _metadataFileProvider;
private ReplServiceProvider _replServiceProvider;
private InteractiveScriptGlobals _globals;
// Session is not thread-safe by itself, and the compilation // Session is not thread-safe by itself, and the compilation
// and execution of scripts are asynchronous operations. // and execution of scripts are asynchronous operations.
...@@ -52,27 +49,44 @@ internal sealed class Service : MarshalByRefObject, IDisposable ...@@ -52,27 +49,44 @@ internal sealed class Service : MarshalByRefObject, IDisposable
private readonly object _lastTaskGuard = new object(); private readonly object _lastTaskGuard = new object();
private Task<EvaluationState> _lastTask; private Task<EvaluationState> _lastTask;
private struct EvaluationState private sealed class ServiceState : IDisposable
{
public readonly InteractiveAssemblyLoader AssemblyLoader;
public readonly MetadataShadowCopyProvider MetadataFileProvider;
public readonly ReplServiceProvider ReplServiceProvider;
public readonly InteractiveScriptGlobals Globals;
public ServiceState(InteractiveAssemblyLoader assemblyLoader, MetadataShadowCopyProvider metadataFileProvider, ReplServiceProvider replServiceProvider, InteractiveScriptGlobals globals)
{
AssemblyLoader = assemblyLoader;
MetadataFileProvider = metadataFileProvider;
ReplServiceProvider = replServiceProvider;
Globals = globals;
}
public void Dispose()
=> MetadataFileProvider.Dispose();
}
private readonly struct EvaluationState
{ {
internal ImmutableArray<string> SourceSearchPaths; internal readonly ImmutableArray<string> SourceSearchPaths;
internal ImmutableArray<string> ReferenceSearchPaths; internal readonly ImmutableArray<string> ReferenceSearchPaths;
internal string WorkingDirectory; internal readonly string WorkingDirectory;
internal readonly ScriptState<object> ScriptStateOpt; internal readonly ScriptState<object>? ScriptState;
internal readonly ScriptOptions ScriptOptions; internal readonly ScriptOptions ScriptOptions;
internal EvaluationState( internal EvaluationState(
ScriptState<object> scriptStateOpt, ScriptState<object>? scriptState,
ScriptOptions scriptOptions, ScriptOptions scriptOptions,
ImmutableArray<string> sourceSearchPaths, ImmutableArray<string> sourceSearchPaths,
ImmutableArray<string> referenceSearchPaths, ImmutableArray<string> referenceSearchPaths,
string workingDirectory) string workingDirectory)
{ {
Debug.Assert(scriptOptions != null);
Debug.Assert(!sourceSearchPaths.IsDefault); Debug.Assert(!sourceSearchPaths.IsDefault);
Debug.Assert(!referenceSearchPaths.IsDefault); Debug.Assert(!referenceSearchPaths.IsDefault);
Debug.Assert(workingDirectory != null);
ScriptStateOpt = scriptStateOpt; ScriptState = scriptState;
ScriptOptions = scriptOptions; ScriptOptions = scriptOptions;
SourceSearchPaths = sourceSearchPaths; SourceSearchPaths = sourceSearchPaths;
ReferenceSearchPaths = referenceSearchPaths; ReferenceSearchPaths = referenceSearchPaths;
...@@ -81,8 +95,6 @@ private struct EvaluationState ...@@ -81,8 +95,6 @@ private struct EvaluationState
internal EvaluationState WithScriptState(ScriptState<object> state) internal EvaluationState WithScriptState(ScriptState<object> state)
{ {
Debug.Assert(state != null);
return new EvaluationState( return new EvaluationState(
state, state,
ScriptOptions, ScriptOptions,
...@@ -93,10 +105,8 @@ internal EvaluationState WithScriptState(ScriptState<object> state) ...@@ -93,10 +105,8 @@ internal EvaluationState WithScriptState(ScriptState<object> state)
internal EvaluationState WithOptions(ScriptOptions options) internal EvaluationState WithOptions(ScriptOptions options)
{ {
Debug.Assert(options != null);
return new EvaluationState( return new EvaluationState(
ScriptStateOpt, ScriptState,
options, options,
SourceSearchPaths, SourceSearchPaths,
ReferenceSearchPaths, ReferenceSearchPaths,
...@@ -115,7 +125,7 @@ internal EvaluationState WithOptions(ScriptOptions options) ...@@ -115,7 +125,7 @@ internal EvaluationState WithOptions(ScriptOptions options)
public Service() public Service()
{ {
var initialState = new EvaluationState( var initialState = new EvaluationState(
scriptStateOpt: null, scriptState: null,
scriptOptions: ScriptOptions.Default, scriptOptions: ScriptOptions.Default,
sourceSearchPaths: ImmutableArray<string>.Empty, sourceSearchPaths: ImmutableArray<string>.Empty,
referenceSearchPaths: ImmutableArray<string>.Empty, referenceSearchPaths: ImmutableArray<string>.Empty,
...@@ -140,34 +150,36 @@ private void HandleProcessExit(object sender, EventArgs e) ...@@ -140,34 +150,36 @@ private void HandleProcessExit(object sender, EventArgs e)
public void Dispose() public void Dispose()
{ {
_metadataFileProvider.Dispose(); _serviceState?.Dispose();
_serviceState = null;
} }
public override object InitializeLifetimeService() public override object? InitializeLifetimeService()
{ {
return null; return null;
} }
public void Initialize(Type replServiceProviderType, string cultureName) public void Initialize(Type replServiceProviderType, string cultureName)
{ {
Debug.Assert(replServiceProviderType != null); Contract.ThrowIfFalse(_serviceState == null, "Service already initialized");
Debug.Assert(cultureName != null);
Debug.Assert(_metadataFileProvider == null);
Debug.Assert(_assemblyLoader == null);
Debug.Assert(_replServiceProvider == null);
// TODO (tomat): we should share the copied files with the host // TODO (tomat): we should share the copied files with the host
_metadataFileProvider = new MetadataShadowCopyProvider( var metadataFileProvider = new MetadataShadowCopyProvider(
Path.Combine(Path.GetTempPath(), "InteractiveHostShadow"), Path.Combine(Path.GetTempPath(), "InteractiveHostShadow"),
noShadowCopyDirectories: s_systemNoShadowCopyDirectories, noShadowCopyDirectories: s_systemNoShadowCopyDirectories,
documentationCommentsCulture: new CultureInfo(cultureName)); documentationCommentsCulture: new CultureInfo(cultureName));
_assemblyLoader = new InteractiveAssemblyLoader(_metadataFileProvider); var assemblyLoader = new InteractiveAssemblyLoader(metadataFileProvider);
var replServiceProvider = (ReplServiceProvider)Activator.CreateInstance(replServiceProviderType);
var globals = new InteractiveScriptGlobals(Console.Out, replServiceProvider.ObjectFormatter);
_replServiceProvider = (ReplServiceProvider)Activator.CreateInstance(replServiceProviderType); _serviceState = new ServiceState(assemblyLoader, metadataFileProvider, replServiceProvider, globals);
}
_globals = new InteractiveScriptGlobals(Console.Out, _replServiceProvider.ObjectFormatter); private ServiceState GetServiceState()
{
Contract.ThrowIfNull(_serviceState, "Service not initialized");
return _serviceState;
} }
private MetadataReferenceResolver CreateMetadataReferenceResolver(ImmutableArray<string> searchPaths, string baseDirectory) private MetadataReferenceResolver CreateMetadataReferenceResolver(ImmutableArray<string> searchPaths, string baseDirectory)
...@@ -177,7 +189,7 @@ private MetadataReferenceResolver CreateMetadataReferenceResolver(ImmutableArray ...@@ -177,7 +189,7 @@ private MetadataReferenceResolver CreateMetadataReferenceResolver(ImmutableArray
packageResolver: null, packageResolver: null,
gacFileResolver: GacFileResolver.IsAvailable ? new GacFileResolver(preferredCulture: CultureInfo.CurrentCulture) : null, gacFileResolver: GacFileResolver.IsAvailable ? new GacFileResolver(preferredCulture: CultureInfo.CurrentCulture) : null,
useCoreResolver: !GacFileResolver.IsAvailable, useCoreResolver: !GacFileResolver.IsAvailable,
fileReferenceProvider: (path, properties) => new ShadowCopyReference(_metadataFileProvider, path, properties)); fileReferenceProvider: (path, properties) => new ShadowCopyReference(GetServiceState().MetadataFileProvider, path, properties));
} }
private SourceReferenceResolver CreateSourceReferenceResolver(ImmutableArray<string> searchPaths, string baseDirectory) private SourceReferenceResolver CreateSourceReferenceResolver(ImmutableArray<string> searchPaths, string baseDirectory)
...@@ -198,10 +210,7 @@ private static bool AttachToClientProcess(int clientProcessId) ...@@ -198,10 +210,7 @@ private static bool AttachToClientProcess(int clientProcessId)
} }
clientProcess.EnableRaisingEvents = true; clientProcess.EnableRaisingEvents = true;
clientProcess.Exited += new EventHandler((_, __) => clientProcess.Exited += new EventHandler((_, __) => s_clientExited.Set());
{
s_clientExited.Set();
});
return clientProcess.IsAlive(); return clientProcess.IsAlive();
} }
...@@ -240,8 +249,8 @@ private static void RunServer(string serverPort, string semaphoreName, int clien ...@@ -240,8 +249,8 @@ private static void RunServer(string serverPort, string semaphoreName, int clien
SetErrorMode(GetErrorMode() | ErrorMode.SEM_FAILCRITICALERRORS | ErrorMode.SEM_NOOPENFILEERRORBOX | ErrorMode.SEM_NOGPFAULTERRORBOX); SetErrorMode(GetErrorMode() | ErrorMode.SEM_FAILCRITICALERRORS | ErrorMode.SEM_NOOPENFILEERRORBOX | ErrorMode.SEM_NOGPFAULTERRORBOX);
} }
IpcServerChannel serverChannel = null; IpcServerChannel? serverChannel = null;
IpcClientChannel clientChannel = null; IpcClientChannel? clientChannel = null;
try try
{ {
using (var semaphore = Semaphore.OpenExisting(semaphoreName)) using (var semaphore = Semaphore.OpenExisting(semaphoreName))
...@@ -325,11 +334,6 @@ private static string GenerateUniqueChannelLocalName() ...@@ -325,11 +334,6 @@ private static string GenerateUniqueChannelLocalName()
string[] sourceSearchPaths, string[] sourceSearchPaths,
string baseDirectory) string baseDirectory)
{ {
Debug.Assert(operation != null);
Debug.Assert(referenceSearchPaths != null);
Debug.Assert(sourceSearchPaths != null);
Debug.Assert(baseDirectory != null);
lock (_lastTaskGuard) lock (_lastTaskGuard)
{ {
_lastTask = SetPathsAsync(_lastTask, operation, referenceSearchPaths, sourceSearchPaths, baseDirectory); _lastTask = SetPathsAsync(_lastTask, operation, referenceSearchPaths, sourceSearchPaths, baseDirectory);
...@@ -343,17 +347,20 @@ private static string GenerateUniqueChannelLocalName() ...@@ -343,17 +347,20 @@ private static string GenerateUniqueChannelLocalName()
string[] sourceSearchPaths, string[] sourceSearchPaths,
string baseDirectory) string baseDirectory)
{ {
var serviceState = GetServiceState();
var state = await ReportUnhandledExceptionIfAnyAsync(lastTask).ConfigureAwait(false); var state = await ReportUnhandledExceptionIfAnyAsync(lastTask).ConfigureAwait(false);
try try
{ {
Directory.SetCurrentDirectory(baseDirectory); Directory.SetCurrentDirectory(baseDirectory);
_globals.ReferencePaths.Clear(); var referencePaths = serviceState.Globals.ReferencePaths;
_globals.ReferencePaths.AddRange(referenceSearchPaths); referencePaths.Clear();
referencePaths.AddRange(referenceSearchPaths);
_globals.SourcePaths.Clear(); var sourcePaths = serviceState.Globals.SourcePaths;
_globals.SourcePaths.AddRange(sourceSearchPaths); sourcePaths.Clear();
sourcePaths.AddRange(sourceSearchPaths);
} }
finally finally
{ {
...@@ -368,10 +375,8 @@ private static string GenerateUniqueChannelLocalName() ...@@ -368,10 +375,8 @@ private static string GenerateUniqueChannelLocalName()
/// Execution is performed on the UI thread. /// Execution is performed on the UI thread.
/// </summary> /// </summary>
[OneWay] [OneWay]
public void InitializeContext(RemoteAsyncOperation<RemoteExecutionResult> operation, string initializationFile, bool isRestarting) public void InitializeContext(RemoteAsyncOperation<RemoteExecutionResult> operation, string? initializationFile, bool isRestarting)
{ {
Debug.Assert(operation != null);
lock (_lastTaskGuard) lock (_lastTaskGuard)
{ {
_lastTask = InitializeContextAsync(_lastTask, operation, initializationFile, isRestarting); _lastTask = InitializeContextAsync(_lastTask, operation, initializationFile, isRestarting);
...@@ -384,9 +389,6 @@ public void InitializeContext(RemoteAsyncOperation<RemoteExecutionResult> operat ...@@ -384,9 +389,6 @@ public void InitializeContext(RemoteAsyncOperation<RemoteExecutionResult> operat
[OneWay] [OneWay]
public void AddReference(RemoteAsyncOperation<bool> operation, string reference) public void AddReference(RemoteAsyncOperation<bool> operation, string reference)
{ {
Debug.Assert(operation != null);
Debug.Assert(reference != null);
lock (_lastTaskGuard) lock (_lastTaskGuard)
{ {
_lastTask = AddReferenceAsync(_lastTask, operation, reference); _lastTask = AddReferenceAsync(_lastTask, operation, reference);
...@@ -429,9 +431,6 @@ private async Task<EvaluationState> AddReferenceAsync(Task<EvaluationState> last ...@@ -429,9 +431,6 @@ private async Task<EvaluationState> AddReferenceAsync(Task<EvaluationState> last
[OneWay] [OneWay]
public void Execute(RemoteAsyncOperation<RemoteExecutionResult> operation, string text) public void Execute(RemoteAsyncOperation<RemoteExecutionResult> operation, string text)
{ {
Debug.Assert(operation != null);
Debug.Assert(text != null);
lock (_lastTaskGuard) lock (_lastTaskGuard)
{ {
_lastTask = ExecuteAsync(_lastTask, operation, text); _lastTask = ExecuteAsync(_lastTask, operation, text);
...@@ -445,7 +444,7 @@ private async Task<EvaluationState> ExecuteAsync(Task<EvaluationState> lastTask, ...@@ -445,7 +444,7 @@ private async Task<EvaluationState> ExecuteAsync(Task<EvaluationState> lastTask,
bool success = false; bool success = false;
try try
{ {
Script<object> script = TryCompile(state.ScriptStateOpt?.Script, text, null, state.ScriptOptions); Script<object>? script = TryCompile(state.ScriptState?.Script, text, null, state.ScriptOptions);
if (script != null) if (script != null)
{ {
// successful if compiled // successful if compiled
...@@ -454,7 +453,7 @@ private async Task<EvaluationState> ExecuteAsync(Task<EvaluationState> lastTask, ...@@ -454,7 +453,7 @@ private async Task<EvaluationState> ExecuteAsync(Task<EvaluationState> lastTask,
// remove references and imports from the options, they have been applied and will be inherited from now on: // remove references and imports from the options, they have been applied and will be inherited from now on:
state = state.WithOptions(state.ScriptOptions.RemoveImportsAndReferences()); state = state.WithOptions(state.ScriptOptions.RemoveImportsAndReferences());
var newScriptState = await ExecuteOnUIThreadAsync(script, state.ScriptStateOpt, displayResult: true).ConfigureAwait(false); var newScriptState = await ExecuteOnUIThreadAsync(script, state.ScriptState, displayResult: true).ConfigureAwait(false);
state = state.WithScriptState(newScriptState); state = state.WithScriptState(newScriptState);
} }
} }
...@@ -478,7 +477,7 @@ private void DisplayException(Exception e) ...@@ -478,7 +477,7 @@ private void DisplayException(Exception e)
} }
else else
{ {
Console.Error.Write(_replServiceProvider.ObjectFormatter.FormatException(e)); Console.Error.Write(GetServiceState().ReplServiceProvider.ObjectFormatter.FormatException(e));
} }
} }
...@@ -488,9 +487,6 @@ private void DisplayException(Exception e) ...@@ -488,9 +487,6 @@ private void DisplayException(Exception e)
[OneWay] [OneWay]
public void ExecuteFile(RemoteAsyncOperation<RemoteExecutionResult> operation, string path) public void ExecuteFile(RemoteAsyncOperation<RemoteExecutionResult> operation, string path)
{ {
Debug.Assert(operation != null);
Debug.Assert(path != null);
lock (_lastTaskGuard) lock (_lastTaskGuard)
{ {
_lastTask = ExecuteFileAsync(operation, _lastTask, path); _lastTask = ExecuteFileAsync(operation, _lastTask, path);
...@@ -500,8 +496,9 @@ public void ExecuteFile(RemoteAsyncOperation<RemoteExecutionResult> operation, s ...@@ -500,8 +496,9 @@ public void ExecuteFile(RemoteAsyncOperation<RemoteExecutionResult> operation, s
private EvaluationState CompleteExecution(EvaluationState state, RemoteAsyncOperation<RemoteExecutionResult> operation, bool success) private EvaluationState CompleteExecution(EvaluationState state, RemoteAsyncOperation<RemoteExecutionResult> operation, bool success)
{ {
// send any updates to the host object and current directory back to the client: // send any updates to the host object and current directory back to the client:
var currentSourcePaths = _globals.SourcePaths.ToArray(); var globals = GetServiceState().Globals;
var currentReferencePaths = _globals.ReferencePaths.ToArray(); var currentSourcePaths = globals.SourcePaths.ToArray();
var currentReferencePaths = globals.ReferencePaths.ToArray();
var currentWorkingDirectory = Directory.GetCurrentDirectory(); var currentWorkingDirectory = Directory.GetCurrentDirectory();
var changedSourcePaths = currentSourcePaths.SequenceEqual(state.SourceSearchPaths) ? null : currentSourcePaths; var changedSourcePaths = currentSourcePaths.SequenceEqual(state.SourceSearchPaths) ? null : currentSourcePaths;
...@@ -532,7 +529,7 @@ private EvaluationState CompleteExecution(EvaluationState state, RemoteAsyncOper ...@@ -532,7 +529,7 @@ private EvaluationState CompleteExecution(EvaluationState state, RemoteAsyncOper
} }
return new EvaluationState( return new EvaluationState(
state.ScriptStateOpt, state.ScriptState,
newOptions, newOptions,
newSourcePaths, newSourcePaths,
newReferencePaths, newReferencePaths,
...@@ -571,11 +568,11 @@ private static void ReportUnhandledException(Exception e) ...@@ -571,11 +568,11 @@ private static void ReportUnhandledException(Exception e)
private async Task<EvaluationState> InitializeContextAsync( private async Task<EvaluationState> InitializeContextAsync(
Task<EvaluationState> lastTask, Task<EvaluationState> lastTask,
RemoteAsyncOperation<RemoteExecutionResult> operation, RemoteAsyncOperation<RemoteExecutionResult> operation,
string initializationFileOpt, string? initializationFile,
bool isRestarting) bool isRestarting)
{ {
Debug.Assert(initializationFileOpt == null || PathUtilities.IsAbsolute(initializationFileOpt)); Contract.ThrowIfFalse(initializationFile == null || PathUtilities.IsAbsolute(initializationFile));
var serviceState = GetServiceState();
var state = await ReportUnhandledExceptionIfAnyAsync(lastTask).ConfigureAwait(false); var state = await ReportUnhandledExceptionIfAnyAsync(lastTask).ConfigureAwait(false);
try try
...@@ -584,18 +581,18 @@ private static void ReportUnhandledException(Exception e) ...@@ -584,18 +581,18 @@ private static void ReportUnhandledException(Exception e)
if (!isRestarting) if (!isRestarting)
{ {
Console.Out.WriteLine(_replServiceProvider.Logo); Console.Out.WriteLine(serviceState.ReplServiceProvider.Logo);
} }
if (File.Exists(initializationFileOpt)) if (File.Exists(initializationFile))
{ {
Console.Out.WriteLine(string.Format(InteractiveHostResources.Loading_context_from_0, Path.GetFileName(initializationFileOpt))); Console.Out.WriteLine(string.Format(InteractiveHostResources.Loading_context_from_0, Path.GetFileName(initializationFile)));
var parser = _replServiceProvider.CommandLineParser; var parser = serviceState.ReplServiceProvider.CommandLineParser;
// The base directory for relative paths is the directory that contains the .rsp file. // The base directory for relative paths is the directory that contains the .rsp file.
// Note that .rsp files included by this .rsp file will share the base directory (Dev10 behavior of csc/vbc). // Note that .rsp files included by this .rsp file will share the base directory (Dev10 behavior of csc/vbc).
var rspDirectory = Path.GetDirectoryName(initializationFileOpt); var rspDirectory = Path.GetDirectoryName(initializationFile);
var args = parser.Parse(new[] { "@" + initializationFileOpt }, rspDirectory, RuntimeEnvironment.GetRuntimeDirectory(), null); var args = parser.Parse(new[] { "@" + initializationFile }, rspDirectory, RuntimeEnvironment.GetRuntimeDirectory(), null);
foreach (var error in args.Errors) foreach (var error in args.Errors)
{ {
...@@ -624,7 +621,7 @@ private static void ReportUnhandledException(Exception e) ...@@ -624,7 +621,7 @@ private static void ReportUnhandledException(Exception e)
var scriptPathOpt = args.SourceFiles.IsEmpty ? null : args.SourceFiles[0].Path; var scriptPathOpt = args.SourceFiles.IsEmpty ? null : args.SourceFiles[0].Path;
var rspState = new EvaluationState( var rspState = new EvaluationState(
state.ScriptStateOpt, state.ScriptState,
state.ScriptOptions. state.ScriptOptions.
WithFilePath(scriptPathOpt). WithFilePath(scriptPathOpt).
WithReferences(metadataReferences). WithReferences(metadataReferences).
...@@ -635,13 +632,14 @@ private static void ReportUnhandledException(Exception e) ...@@ -635,13 +632,14 @@ private static void ReportUnhandledException(Exception e)
args.ReferencePaths, args.ReferencePaths,
rspDirectory); rspDirectory);
_globals.ReferencePaths.Clear(); var globals = serviceState.Globals;
_globals.ReferencePaths.AddRange(args.ReferencePaths); globals.ReferencePaths.Clear();
globals.ReferencePaths.AddRange(args.ReferencePaths);
_globals.SourcePaths.Clear(); globals.SourcePaths.Clear();
_globals.SourcePaths.AddRange(args.SourcePaths); globals.SourcePaths.AddRange(args.SourcePaths);
_globals.Args.AddRange(args.ScriptArguments); globals.Args.AddRange(args.ScriptArguments);
if (scriptPathOpt != null) if (scriptPathOpt != null)
{ {
...@@ -676,16 +674,16 @@ private static void ReportUnhandledException(Exception e) ...@@ -676,16 +674,16 @@ private static void ReportUnhandledException(Exception e)
return state; return state;
} }
private string ResolveRelativePath(string path, string baseDirectory, ImmutableArray<string> searchPaths, bool displayPath) private string? ResolveRelativePath(string path, string baseDirectory, ImmutableArray<string> searchPaths, bool displayPath)
{ {
List<string> attempts = new List<string>(); var attempts = new List<string>();
bool fileExists(string file) bool fileExists(string file)
{ {
attempts.Add(file); attempts.Add(file);
return File.Exists(file); return File.Exists(file);
} }
string fullPath = FileUtilities.ResolveRelativePath(path, null, baseDirectory, searchPaths, fileExists); var fullPath = FileUtilities.ResolveRelativePath(path, null, baseDirectory, searchPaths, fileExists);
if (fullPath == null) if (fullPath == null)
{ {
if (displayPath) if (displayPath)
...@@ -706,8 +704,10 @@ bool fileExists(string file) ...@@ -706,8 +704,10 @@ bool fileExists(string file)
return fullPath; return fullPath;
} }
private Script<object> TryCompile(Script previousScript, string code, string path, ScriptOptions options) private Script<object>? TryCompile(Script? previousScript, string code, string? path, ScriptOptions options)
{ {
var serviceState = GetServiceState();
Script script; Script script;
var scriptOptions = options.WithFilePath(path); var scriptOptions = options.WithFilePath(path);
...@@ -718,7 +718,7 @@ private Script<object> TryCompile(Script previousScript, string code, string pat ...@@ -718,7 +718,7 @@ private Script<object> TryCompile(Script previousScript, string code, string pat
} }
else else
{ {
script = _replServiceProvider.CreateScript<object>(code, scriptOptions, _globals.GetType(), _assemblyLoader); script = serviceState.ReplServiceProvider.CreateScript<object>(code, scriptOptions, serviceState.Globals.GetType(), serviceState.AssemblyLoader);
} }
var diagnostics = script.Compile(); var diagnostics = script.Compile();
...@@ -737,7 +737,7 @@ private Script<object> TryCompile(Script previousScript, string code, string pat ...@@ -737,7 +737,7 @@ private Script<object> TryCompile(Script previousScript, string code, string pat
string path) string path)
{ {
var state = await ReportUnhandledExceptionIfAnyAsync(lastTask).ConfigureAwait(false); var state = await ReportUnhandledExceptionIfAnyAsync(lastTask).ConfigureAwait(false);
string fullPath = ResolveRelativePath(path, state.WorkingDirectory, state.SourceSearchPaths, displayPath: false); var fullPath = ResolveRelativePath(path, state.WorkingDirectory, state.SourceSearchPaths, displayPath: false);
if (fullPath != null) if (fullPath != null)
{ {
var newScriptState = await TryExecuteFileAsync(state, fullPath).ConfigureAwait(false); var newScriptState = await TryExecuteFileAsync(state, fullPath).ConfigureAwait(false);
...@@ -757,17 +757,15 @@ private Script<object> TryCompile(Script previousScript, string code, string pat ...@@ -757,17 +757,15 @@ private Script<object> TryCompile(Script previousScript, string code, string pat
/// All errors are written to the error output stream. /// All errors are written to the error output stream.
/// Uses source search paths to resolve unrooted paths. /// Uses source search paths to resolve unrooted paths.
/// </remarks> /// </remarks>
private async Task<ScriptState<object>> TryExecuteFileAsync(EvaluationState state, string fullPath) private async Task<ScriptState<object>?> TryExecuteFileAsync(EvaluationState state, string fullPath)
{ {
Debug.Assert(PathUtilities.IsAbsolute(fullPath)); Debug.Assert(PathUtilities.IsAbsolute(fullPath));
string content = null; string? content = null;
try try
{ {
using (var reader = File.OpenText(fullPath)) using var reader = File.OpenText(fullPath);
{ content = await reader.ReadToEndAsync().ConfigureAwait(false);
content = await reader.ReadToEndAsync().ConfigureAwait(false);
}
} }
catch (Exception e) catch (Exception e)
{ {
...@@ -776,14 +774,14 @@ private async Task<ScriptState<object>> TryExecuteFileAsync(EvaluationState stat ...@@ -776,14 +774,14 @@ private async Task<ScriptState<object>> TryExecuteFileAsync(EvaluationState stat
return null; return null;
} }
Script<object> script = TryCompile(state.ScriptStateOpt?.Script, content, fullPath, state.ScriptOptions); Script<object>? script = TryCompile(state.ScriptState?.Script, content, fullPath, state.ScriptOptions);
if (script == null) if (script == null)
{ {
// compilation errors: // compilation errors:
return null; return null;
} }
return await ExecuteOnUIThreadAsync(script, state.ScriptStateOpt, displayResult: false).ConfigureAwait(false); return await ExecuteOnUIThreadAsync(script, state.ScriptState, displayResult: false).ConfigureAwait(false);
} }
private static void DisplaySearchPaths(TextWriter writer, List<string> attemptedFilePaths) private static void DisplaySearchPaths(TextWriter writer, List<string> attemptedFilePaths)
...@@ -805,14 +803,18 @@ private static void DisplaySearchPaths(TextWriter writer, List<string> attempted ...@@ -805,14 +803,18 @@ private static void DisplaySearchPaths(TextWriter writer, List<string> attempted
} }
} }
private async Task<ScriptState<object>> ExecuteOnUIThreadAsync(Script<object> script, ScriptState<object> stateOpt, bool displayResult) private async Task<ScriptState<object>> ExecuteOnUIThreadAsync(Script<object> script, ScriptState<object>? state, bool displayResult)
{ {
Contract.ThrowIfNull(s_control, "UI thread not initialized");
return await ((Task<ScriptState<object>>)s_control.Invoke( return await ((Task<ScriptState<object>>)s_control.Invoke(
(Func<Task<ScriptState<object>>>)(async () => (Func<Task<ScriptState<object>>>)(async () =>
{ {
var task = (stateOpt == null) ? var serviceState = GetServiceState();
script.RunAsync(_globals, catchException: e => true, cancellationToken: CancellationToken.None) :
script.RunFromAsync(stateOpt, catchException: e => true, cancellationToken: CancellationToken.None); var task = (state == null) ?
script.RunAsync(serviceState.Globals, catchException: e => true, cancellationToken: CancellationToken.None) :
script.RunFromAsync(state, catchException: e => true, cancellationToken: CancellationToken.None);
var newState = await task.ConfigureAwait(false); var newState = await task.ConfigureAwait(false);
...@@ -822,7 +824,7 @@ private async Task<ScriptState<object>> ExecuteOnUIThreadAsync(Script<object> sc ...@@ -822,7 +824,7 @@ private async Task<ScriptState<object>> ExecuteOnUIThreadAsync(Script<object> sc
} }
else if (displayResult && newState.Script.HasReturnValue()) else if (displayResult && newState.Script.HasReturnValue())
{ {
_globals.Print(newState.ReturnValue); serviceState.Globals.Print(newState.ReturnValue);
} }
return newState; return newState;
...@@ -841,7 +843,7 @@ private void DisplayInteractiveErrors(ImmutableArray<Diagnostic> diagnostics, Te ...@@ -841,7 +843,7 @@ private void DisplayInteractiveErrors(ImmutableArray<Diagnostic> diagnostics, Te
displayedDiagnostics.Sort((d1, d2) => d1.Location.SourceSpan.Start - d2.Location.SourceSpan.Start); displayedDiagnostics.Sort((d1, d2) => d1.Location.SourceSpan.Start - d2.Location.SourceSpan.Start);
var formatter = _replServiceProvider.DiagnosticFormatter; var formatter = GetServiceState().ReplServiceProvider.DiagnosticFormatter;
foreach (var diagnostic in displayedDiagnostics) foreach (var diagnostic in displayedDiagnostics)
{ {
...@@ -929,11 +931,6 @@ public void RemoteConsoleWrite(byte[] data, bool isError) ...@@ -929,11 +931,6 @@ public void RemoteConsoleWrite(byte[] data, bool isError)
} }
} }
public bool IsShadowCopy(string path)
{
return _metadataFileProvider.IsShadowCopy(path);
}
#endregion #endregion
} }
} }
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System.Diagnostics; #nullable enable
using Microsoft.CodeAnalysis.Scripting.Hosting; using Microsoft.CodeAnalysis.Scripting.Hosting;
namespace Microsoft.CodeAnalysis.Interactive namespace Microsoft.CodeAnalysis.Interactive
...@@ -20,9 +21,6 @@ private sealed class ShadowCopyReference : PortableExecutableReference ...@@ -20,9 +21,6 @@ private sealed class ShadowCopyReference : PortableExecutableReference
public ShadowCopyReference(MetadataShadowCopyProvider provider, string originalPath, MetadataReferenceProperties properties) public ShadowCopyReference(MetadataShadowCopyProvider provider, string originalPath, MetadataReferenceProperties properties)
: base(properties, originalPath) : base(properties, originalPath)
{ {
Debug.Assert(originalPath != null);
Debug.Assert(provider != null);
_provider = provider; _provider = provider;
} }
...@@ -39,7 +37,7 @@ protected override Metadata GetMetadataImpl() ...@@ -39,7 +37,7 @@ protected override Metadata GetMetadataImpl()
protected override PortableExecutableReference WithPropertiesImpl(MetadataReferenceProperties properties) protected override PortableExecutableReference WithPropertiesImpl(MetadataReferenceProperties properties)
{ {
return new ShadowCopyReference(_provider, this.FilePath, properties); return new ShadowCopyReference(_provider, FilePath!, properties);
} }
} }
} }
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
...@@ -24,7 +26,7 @@ namespace Microsoft.CodeAnalysis.Interactive ...@@ -24,7 +26,7 @@ namespace Microsoft.CodeAnalysis.Interactive
/// <remarks> /// <remarks>
/// Handles spawning of the host process and communication between the local callers and the remote session. /// Handles spawning of the host process and communication between the local callers and the remote session.
/// </remarks> /// </remarks>
internal sealed partial class InteractiveHost : MarshalByRefObject internal sealed partial class InteractiveHost
{ {
internal const bool DefaultIs64Bit = true; internal const bool DefaultIs64Bit = true;
...@@ -35,11 +37,11 @@ internal sealed partial class InteractiveHost : MarshalByRefObject ...@@ -35,11 +37,11 @@ internal sealed partial class InteractiveHost : MarshalByRefObject
private readonly int _millisecondsTimeout; private readonly int _millisecondsTimeout;
private const int MaxAttemptsToCreateProcess = 2; private const int MaxAttemptsToCreateProcess = 2;
private LazyRemoteService _lazyRemoteService; private LazyRemoteService? _lazyRemoteService;
private int _remoteServiceInstanceId; private int _remoteServiceInstanceId;
// Remoting channel to communicate with the remote service. // Remoting channel to communicate with the remote service.
private IpcServerChannel _serverChannel; private IpcServerChannel? _serverChannel;
private TextWriter _output; private TextWriter _output;
private TextWriter _errorOutput; private TextWriter _errorOutput;
...@@ -55,7 +57,7 @@ internal sealed partial class InteractiveHost : MarshalByRefObject ...@@ -55,7 +57,7 @@ internal sealed partial class InteractiveHost : MarshalByRefObject
/// </remarks> /// </remarks>
private readonly bool _joinOutputWritingThreadsOnDisposal; private readonly bool _joinOutputWritingThreadsOnDisposal;
internal event Action<bool> ProcessStarting; internal event Action<bool>? ProcessStarting;
public InteractiveHost( public InteractiveHost(
Type replServiceProviderType, Type replServiceProviderType,
...@@ -79,49 +81,39 @@ internal sealed partial class InteractiveHost : MarshalByRefObject ...@@ -79,49 +81,39 @@ internal sealed partial class InteractiveHost : MarshalByRefObject
#region Test hooks #region Test hooks
internal event Action<char[], int> OutputReceived; internal event Action<char[], int>? OutputReceived;
internal event Action<char[], int> ErrorOutputReceived; internal event Action<char[], int>? ErrorOutputReceived;
internal Process TryGetProcess() internal Process? TryGetProcess()
{ {
InitializedRemoteService initializedService;
var lazyRemoteService = _lazyRemoteService; var lazyRemoteService = _lazyRemoteService;
return (lazyRemoteService?.InitializedService != null && return (lazyRemoteService?.InitializedService != null &&
lazyRemoteService.InitializedService.TryGetValue(out initializedService)) ? initializedService.ServiceOpt.Process : null; lazyRemoteService.InitializedService.TryGetValue(out var initializedService)) ? initializedService.Service.Process : null;
} }
internal Service TryGetService() internal Service? TryGetService()
{ {
var initializedService = TryGetOrCreateRemoteServiceAsync().Result; var initializedService = TryGetOrCreateRemoteServiceAsync().Result;
return initializedService.ServiceOpt?.Service; return initializedService.Service?.Service;
} }
// Triggered whenever we create a fresh process. // Triggered whenever we create a fresh process.
// The ProcessExited event is not hooked yet. // The ProcessExited event is not hooked yet.
internal event Action<Process> InteractiveHostProcessCreated; internal event Action<Process>? InteractiveHostProcessCreated;
internal IpcServerChannel _ServerChannel internal IpcServerChannel? Test_ServerChannel
{ => _serverChannel;
get { return _serverChannel; }
}
#endregion #endregion
private static string GenerateUniqueChannelLocalName() private static string GenerateUniqueChannelLocalName()
{ => typeof(InteractiveHost).FullName + Guid.NewGuid();
return typeof(InteractiveHost).FullName + Guid.NewGuid();
}
public override object InitializeLifetimeService() private RemoteService? TryStartProcess(string hostPath, CultureInfo culture, CancellationToken cancellationToken)
{ {
return null; Process? newProcess = null;
}
private RemoteService TryStartProcess(string hostPath, CultureInfo culture, CancellationToken cancellationToken)
{
Process newProcess = null;
int newProcessId = -1; int newProcessId = -1;
Semaphore semaphore = null; Semaphore? semaphore = null;
try try
{ {
int currentProcessId = Process.GetCurrentProcess().Id; int currentProcessId = Process.GetCurrentProcess().Id;
...@@ -309,7 +301,7 @@ internal void OnOutputReceived(bool error, char[] buffer, int count) ...@@ -309,7 +301,7 @@ internal void OnOutputReceived(bool error, char[] buffer, int count)
} }
} }
private void WriteOutputInBackground(bool isError, string firstLine, string secondLine = null) private void WriteOutputInBackground(bool isError, string firstLine, string? secondLine = null)
{ {
var writer = isError ? _errorOutput : _output; var writer = isError ? _errorOutput : _output;
var guard = isError ? _errorOutputGuard : _outputGuard; var guard = isError ? _errorOutputGuard : _outputGuard;
...@@ -364,7 +356,7 @@ private async Task<InitializedRemoteService> TryGetOrCreateRemoteServiceAsync() ...@@ -364,7 +356,7 @@ private async Task<InitializedRemoteService> TryGetOrCreateRemoteServiceAsync()
{ {
try try
{ {
LazyRemoteService currentRemoteService = _lazyRemoteService; LazyRemoteService? currentRemoteService = _lazyRemoteService;
for (int attempt = 0; attempt < MaxAttemptsToCreateProcess; attempt++) for (int attempt = 0; attempt < MaxAttemptsToCreateProcess; attempt++)
{ {
...@@ -375,7 +367,7 @@ private async Task<InitializedRemoteService> TryGetOrCreateRemoteServiceAsync() ...@@ -375,7 +367,7 @@ private async Task<InitializedRemoteService> TryGetOrCreateRemoteServiceAsync()
} }
var initializedService = await currentRemoteService.InitializedService.GetValueAsync(currentRemoteService.CancellationSource.Token).ConfigureAwait(false); var initializedService = await currentRemoteService.InitializedService.GetValueAsync(currentRemoteService.CancellationSource.Token).ConfigureAwait(false);
if (initializedService.ServiceOpt != null && initializedService.ServiceOpt.Process.IsAlive()) if (initializedService.Service != null && initializedService.Service.Process.IsAlive())
{ {
return initializedService; return initializedService;
} }
...@@ -418,12 +410,12 @@ private async Task<TResult> Async<TResult>(Action<Service, RemoteAsyncOperation< ...@@ -418,12 +410,12 @@ private async Task<TResult> Async<TResult>(Action<Service, RemoteAsyncOperation<
try try
{ {
var initializedService = await TryGetOrCreateRemoteServiceAsync().ConfigureAwait(false); var initializedService = await TryGetOrCreateRemoteServiceAsync().ConfigureAwait(false);
if (initializedService.ServiceOpt == null) if (initializedService.Service == null)
{ {
return default; return default!;
} }
return await new RemoteAsyncOperation<TResult>(initializedService.ServiceOpt).ExecuteAsync(action).ConfigureAwait(false); return await new RemoteAsyncOperation<TResult>(initializedService.Service).ExecuteAsync(action).ConfigureAwait(false);
} }
catch (Exception e) when (FatalError.Report(e)) catch (Exception e) when (FatalError.Report(e))
{ {
...@@ -445,7 +437,7 @@ private static async Task<TResult> Async<TResult>(RemoteService remoteService, A ...@@ -445,7 +437,7 @@ private static async Task<TResult> Async<TResult>(RemoteService remoteService, A
#region Operations #region Operations
public InteractiveHostOptions OptionsOpt public InteractiveHostOptions? OptionsOpt
=> _lazyRemoteService?.Options; => _lazyRemoteService?.Options;
/// <summary> /// <summary>
...@@ -454,8 +446,6 @@ public InteractiveHostOptions OptionsOpt ...@@ -454,8 +446,6 @@ public InteractiveHostOptions OptionsOpt
/// <param name="options">The options to initialize the new process with.</param> /// <param name="options">The options to initialize the new process with.</param>
public async Task<RemoteExecutionResult> ResetAsync(InteractiveHostOptions options) public async Task<RemoteExecutionResult> ResetAsync(InteractiveHostOptions options)
{ {
Debug.Assert(options != null);
try try
{ {
// replace the existing service with a new one: // replace the existing service with a new one:
...@@ -468,7 +458,7 @@ public async Task<RemoteExecutionResult> ResetAsync(InteractiveHostOptions optio ...@@ -468,7 +458,7 @@ public async Task<RemoteExecutionResult> ResetAsync(InteractiveHostOptions optio
} }
var initializedService = await TryGetOrCreateRemoteServiceAsync().ConfigureAwait(false); var initializedService = await TryGetOrCreateRemoteServiceAsync().ConfigureAwait(false);
if (initializedService.ServiceOpt == null) if (initializedService.Service == null)
{ {
return default; return default;
} }
...@@ -491,7 +481,7 @@ public async Task<RemoteExecutionResult> ResetAsync(InteractiveHostOptions optio ...@@ -491,7 +481,7 @@ public async Task<RemoteExecutionResult> ResetAsync(InteractiveHostOptions optio
/// </remarks> /// </remarks>
public Task<RemoteExecutionResult> ExecuteAsync(string code) public Task<RemoteExecutionResult> ExecuteAsync(string code)
{ {
Debug.Assert(code != null); Contract.ThrowIfNull(code);
return Async<RemoteExecutionResult>((service, operation) => service.Execute(operation, code)); return Async<RemoteExecutionResult>((service, operation) => service.Execute(operation, code));
} }
...@@ -506,11 +496,7 @@ public Task<RemoteExecutionResult> ExecuteAsync(string code) ...@@ -506,11 +496,7 @@ public Task<RemoteExecutionResult> ExecuteAsync(string code)
/// </remarks> /// </remarks>
public Task<RemoteExecutionResult> ExecuteFileAsync(string path) public Task<RemoteExecutionResult> ExecuteFileAsync(string path)
{ {
if (path == null) Contract.ThrowIfNull(path);
{
throw new ArgumentNullException(nameof(path));
}
return Async<RemoteExecutionResult>((service, operation) => service.ExecuteFile(operation, path)); return Async<RemoteExecutionResult>((service, operation) => service.ExecuteFile(operation, path));
} }
...@@ -524,7 +510,7 @@ public Task<RemoteExecutionResult> ExecuteFileAsync(string path) ...@@ -524,7 +510,7 @@ public Task<RemoteExecutionResult> ExecuteFileAsync(string path)
/// </remarks> /// </remarks>
public Task<bool> AddReferenceAsync(string reference) public Task<bool> AddReferenceAsync(string reference)
{ {
Debug.Assert(reference != null); Contract.ThrowIfNull(reference);
return Async<bool>((service, operation) => service.AddReference(operation, reference)); return Async<bool>((service, operation) => service.AddReference(operation, reference));
} }
...@@ -533,9 +519,9 @@ public Task<bool> AddReferenceAsync(string reference) ...@@ -533,9 +519,9 @@ public Task<bool> AddReferenceAsync(string reference)
/// </summary> /// </summary>
public Task<RemoteExecutionResult> SetPathsAsync(string[] referenceSearchPaths, string[] sourceSearchPaths, string baseDirectory) public Task<RemoteExecutionResult> SetPathsAsync(string[] referenceSearchPaths, string[] sourceSearchPaths, string baseDirectory)
{ {
Debug.Assert(referenceSearchPaths != null); Contract.ThrowIfNull(referenceSearchPaths);
Debug.Assert(sourceSearchPaths != null); Contract.ThrowIfNull(sourceSearchPaths);
Debug.Assert(baseDirectory != null); Contract.ThrowIfNull(baseDirectory);
return Async<RemoteExecutionResult>((service, operation) => service.SetPaths(operation, referenceSearchPaths, sourceSearchPaths, baseDirectory)); return Async<RemoteExecutionResult>((service, operation) => service.SetPaths(operation, referenceSearchPaths, sourceSearchPaths, baseDirectory));
} }
......
...@@ -2,10 +2,13 @@ ...@@ -2,10 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Interactive namespace Microsoft.CodeAnalysis.Interactive
{ {
...@@ -17,7 +20,7 @@ internal sealed class InteractiveHostOptions ...@@ -17,7 +20,7 @@ internal sealed class InteractiveHostOptions
/// <summary> /// <summary>
/// Optional path to the .rsp file to process when initializing context of the process. /// Optional path to the .rsp file to process when initializing context of the process.
/// </summary> /// </summary>
public string InitializationFile { get; } public string? InitializationFile { get; }
/// <summary> /// <summary>
/// Host culture used for localization of doc comments, errors. /// Host culture used for localization of doc comments, errors.
...@@ -36,11 +39,11 @@ internal sealed class InteractiveHostOptions ...@@ -36,11 +39,11 @@ internal sealed class InteractiveHostOptions
public InteractiveHostOptions( public InteractiveHostOptions(
string hostDirectory, string hostDirectory,
string initializationFile = null, string? initializationFile = null,
CultureInfo culture = null, CultureInfo? culture = null,
bool is64Bit = false) bool is64Bit = false)
{ {
Debug.Assert(hostDirectory != null); Contract.ThrowIfNull(hostDirectory);
HostDirectory = hostDirectory; HostDirectory = hostDirectory;
InitializationFile = initializationFile; InitializationFile = initializationFile;
Culture = culture ?? CultureInfo.CurrentUICulture; Culture = culture ?? CultureInfo.CurrentUICulture;
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
using System.Diagnostics; using System.Diagnostics;
namespace Microsoft.CodeAnalysis.Interactive namespace Microsoft.CodeAnalysis.Interactive
......
...@@ -2,40 +2,42 @@ ...@@ -2,40 +2,42 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
using System; using System;
namespace Microsoft.CodeAnalysis.Interactive namespace Microsoft.CodeAnalysis.Interactive
{ {
[Serializable] [Serializable]
internal struct RemoteExecutionResult internal readonly struct RemoteExecutionResult
{ {
public readonly bool Success; public readonly bool Success;
/// <summary> /// <summary>
/// New value of source search paths after execution, or null if not changed since the last execution. /// New value of source search paths after execution, or null if not changed since the last execution.
/// </summary> /// </summary>
public readonly string[] ChangedSourcePaths; public readonly string[]? ChangedSourcePaths;
/// <summary> /// <summary>
/// New value of reference search paths after execution, or null if not changed since the last execution. /// New value of reference search paths after execution, or null if not changed since the last execution.
/// </summary> /// </summary>
public readonly string[] ChangedReferencePaths; public readonly string[]? ChangedReferencePaths;
/// <summary> /// <summary>
/// New value of working directory in the remote process after execution, or null if not changed since the last execution. /// New value of working directory in the remote process after execution, or null if not changed since the last execution.
/// </summary> /// </summary>
public readonly string ChangedWorkingDirectory; public readonly string? ChangedWorkingDirectory;
public RemoteExecutionResult( public RemoteExecutionResult(
bool success, bool success,
string[] changedSourcePaths = null, string[]? changedSourcePaths = null,
string[] changedReferencePaths = null, string[]? changedReferencePaths = null,
string changedWorkingDirectory = null) string? changedWorkingDirectory = null)
{ {
this.Success = success; Success = success;
this.ChangedSourcePaths = changedSourcePaths; ChangedSourcePaths = changedSourcePaths;
this.ChangedReferencePaths = changedReferencePaths; ChangedReferencePaths = changedReferencePaths;
this.ChangedWorkingDirectory = changedWorkingDirectory; ChangedWorkingDirectory = changedWorkingDirectory;
} }
} }
} }
// Licensed to the .NET Foundation under one or more agreements. // Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
extern alias InteractiveHost; extern alias InteractiveHost;
using System; using System;
...@@ -32,7 +35,7 @@ internal static string GetInteractiveHostDirectory() ...@@ -32,7 +35,7 @@ internal static string GetInteractiveHostDirectory()
internal static void DisposeInteractiveHostProcess(InteractiveHost host) internal static void DisposeInteractiveHostProcess(InteractiveHost host)
{ {
var serverChannel = host._ServerChannel; var serverChannel = host.Test_ServerChannel;
host.Dispose(); host.Dispose();
var listenerThread = (Thread)s_ipcServerChannelListenerThread.GetValue(serverChannel); var listenerThread = (Thread)s_ipcServerChannelListenerThread.GetValue(serverChannel);
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
extern alias InteractiveHost; extern alias InteractiveHost;
using System; using System;
...@@ -34,8 +36,8 @@ public sealed class InteractiveHostTests : AbstractInteractiveHostTests ...@@ -34,8 +36,8 @@ public sealed class InteractiveHostTests : AbstractInteractiveHostTests
{ {
#region Utils #region Utils
private SynchronizedStringWriter _synchronizedOutput; private SynchronizedStringWriter _synchronizedOutput = null!;
private SynchronizedStringWriter _synchronizedErrorOutput; private SynchronizedStringWriter _synchronizedErrorOutput = null!;
private int[] _outputReadPosition = new int[] { 0, 0 }; private int[] _outputReadPosition = new int[] { 0, 0 };
private readonly InteractiveHost _host; private readonly InteractiveHost _host;
...@@ -75,7 +77,7 @@ public override void Dispose() ...@@ -75,7 +77,7 @@ public override void Dispose()
{ {
try try
{ {
Process process = _host.TryGetProcess(); var process = _host.TryGetProcess();
DisposeInteractiveHostProcess(_host); DisposeInteractiveHostProcess(_host);
...@@ -118,11 +120,6 @@ private bool Execute(string code) ...@@ -118,11 +120,6 @@ private bool Execute(string code)
return task.Result.Success; return task.Result.Success;
} }
private bool IsShadowCopy(string path)
{
return _host.TryGetService().IsShadowCopy(path);
}
public string ReadErrorOutputToEnd() public string ReadErrorOutputToEnd()
{ {
return ReadOutputToEnd(isError: true); return ReadOutputToEnd(isError: true);
...@@ -135,7 +132,7 @@ private void ClearOutput() ...@@ -135,7 +132,7 @@ private void ClearOutput()
_synchronizedErrorOutput.Clear(); _synchronizedErrorOutput.Clear();
} }
private void RestartHost(string rspFile = null) private void RestartHost(string? rspFile = null)
{ {
ClearOutput(); ClearOutput();
...@@ -150,7 +147,7 @@ public string ReadOutputToEnd(bool isError = false) ...@@ -150,7 +147,7 @@ public string ReadOutputToEnd(bool isError = false)
var mark = markPrefix + Guid.NewGuid().ToString(); var mark = markPrefix + Guid.NewGuid().ToString();
// writes mark to the STDOUT/STDERR pipe in the remote process: // writes mark to the STDOUT/STDERR pipe in the remote process:
_host.TryGetService().RemoteConsoleWrite(Encoding.UTF8.GetBytes(mark), isError); _host.TryGetService()!.RemoteConsoleWrite(Encoding.UTF8.GetBytes(mark), isError);
while (true) while (true)
{ {
...@@ -164,13 +161,8 @@ public string ReadOutputToEnd(bool isError = false) ...@@ -164,13 +161,8 @@ public string ReadOutputToEnd(bool isError = false)
} }
} }
private class CompiledFile private static (string Path, ImmutableArray<byte> Image) CompileLibrary(
{ TempDirectory dir, string fileName, string assemblyName, string source, params MetadataReference[] references)
public string Path;
public ImmutableArray<byte> Image;
}
private static CompiledFile CompileLibrary(TempDirectory dir, string fileName, string assemblyName, string source, params MetadataReference[] references)
{ {
var file = dir.CreateFile(fileName); var file = dir.CreateFile(fileName);
var compilation = CreateEmptyCompilation( var compilation = CreateEmptyCompilation(
...@@ -182,7 +174,7 @@ private static CompiledFile CompileLibrary(TempDirectory dir, string fileName, s ...@@ -182,7 +174,7 @@ private static CompiledFile CompileLibrary(TempDirectory dir, string fileName, s
var image = compilation.EmitToArray(); var image = compilation.EmitToArray();
file.WriteAllBytes(image); file.WriteAllBytes(image);
return new CompiledFile { Path = file.Path, Image = image }; return (file.Path, image);
} }
#endregion #endregion
...@@ -250,7 +242,7 @@ int goo(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, ...@@ -250,7 +242,7 @@ int goo(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8,
// Hosting process exited with exit code ###. // Hosting process exited with exit code ###.
var errorOutput = ReadErrorOutputToEnd().Trim(); var errorOutput = ReadErrorOutputToEnd().Trim();
Assert.Equal("Process is terminated due to StackOverflowException.\n" + string.Format(InteractiveHostResources.Hosting_process_exited_with_exit_code_0, process.ExitCode), errorOutput); Assert.Equal("Process is terminated due to StackOverflowException.\n" + string.Format(InteractiveHostResources.Hosting_process_exited_with_exit_code_0, process!.ExitCode), errorOutput);
Execute(@"1+1"); Execute(@"1+1");
...@@ -337,10 +329,10 @@ public void AsyncExecute_HangingForegroundThreads() ...@@ -337,10 +329,10 @@ public void AsyncExecute_HangingForegroundThreads()
var process = _host.TryGetProcess(); var process = _host.TryGetProcess();
Assert.NotNull(process); Assert.NotNull(process);
service.EmulateClientExit(); service!.EmulateClientExit();
// the process should terminate with exit code 0: // the process should terminate with exit code 0:
process.WaitForExit(); process!.WaitForExit();
Assert.Equal(0, process.ExitCode); Assert.Equal(0, process.ExitCode);
} }
...@@ -454,7 +446,7 @@ public void UserDefinedAssemblyResolve_InfiniteLoop() ...@@ -454,7 +446,7 @@ public void UserDefinedAssemblyResolve_InfiniteLoop()
var mayTerminate = new ManualResetEvent(false); var mayTerminate = new ManualResetEvent(false);
_host.ErrorOutputReceived += (_, __) => mayTerminate.Set(); _host.ErrorOutputReceived += (_, __) => mayTerminate.Set();
_host.TryGetService().HookMaliciousAssemblyResolve(); _host.TryGetService()!.HookMaliciousAssemblyResolve();
var executeTask = _host.AddReferenceAsync("nonexistingassembly" + Guid.NewGuid()); var executeTask = _host.AddReferenceAsync("nonexistingassembly" + Guid.NewGuid());
Assert.True(mayTerminate.WaitOne()); Assert.True(mayTerminate.WaitOne());
......
// Licensed to the .NET Foundation under one or more agreements. // Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
extern alias InteractiveHost; extern alias InteractiveHost;
using System; using System;
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
#nullable enable
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.UnitTests.Interactive namespace Microsoft.CodeAnalysis.UnitTests.Interactive
{ {
...@@ -44,7 +45,7 @@ public override string ToString() ...@@ -44,7 +45,7 @@ public override string ToString()
} }
} }
public string Prefix(string mark, ref int start) public string? Prefix(string mark, ref int start)
{ {
Debug.Assert(!string.IsNullOrEmpty(mark)); Debug.Assert(!string.IsNullOrEmpty(mark));
......
...@@ -223,8 +223,8 @@ public static IEnumerable<TSymbol> FindSimilarSymbols<TSymbol>(TSymbol symbol, C ...@@ -223,8 +223,8 @@ public static IEnumerable<TSymbol> FindSimilarSymbols<TSymbol>(TSymbol symbol, C
} }
/// <summary> /// <summary>
/// If <paramref name="symbol"/> is declared in a linked file, then this function returns all the other symbols /// If <paramref name="symbol"/> is declared in a linked file, then this function returns all the symbols that
/// that are defined by the same symbol's syntax in the other projects that the linked file is referenced from. /// are defined by the same symbol's syntax in the all projects that the linked file is referenced from.
/// <para/> /// <para/>
/// In order to be returned the other symbols must have the same <see cref="ISymbol.Name"/> and <see /// In order to be returned the other symbols must have the same <see cref="ISymbol.Name"/> and <see
/// cref="ISymbol.Kind"/> as <paramref name="symbol"/>. This matches general user intuition that these are all /// cref="ISymbol.Kind"/> as <paramref name="symbol"/>. This matches general user intuition that these are all
...@@ -234,7 +234,8 @@ public static IEnumerable<TSymbol> FindSimilarSymbols<TSymbol>(TSymbol symbol, C ...@@ -234,7 +234,8 @@ public static IEnumerable<TSymbol> FindSimilarSymbols<TSymbol>(TSymbol symbol, C
internal static async Task<ImmutableArray<ISymbol>> FindLinkedSymbolsAsync( internal static async Task<ImmutableArray<ISymbol>> FindLinkedSymbolsAsync(
ISymbol symbol, Solution solution, CancellationToken cancellationToken) ISymbol symbol, Solution solution, CancellationToken cancellationToken)
{ {
var linkedSymbols = new HashSet<ISymbol>(); // Add the original symbol to the result set.
var linkedSymbols = new HashSet<ISymbol> { symbol };
foreach (var location in symbol.DeclaringSyntaxReferences) foreach (var location in symbol.DeclaringSyntaxReferences)
{ {
...@@ -256,8 +257,9 @@ public static IEnumerable<TSymbol> FindSimilarSymbols<TSymbol>(TSymbol symbol, C ...@@ -256,8 +257,9 @@ public static IEnumerable<TSymbol> FindSimilarSymbols<TSymbol>(TSymbol symbol, C
var semanticModel = await linkedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var semanticModel = await linkedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var linkedSymbol = semanticModel.GetDeclaredSymbol(linkedNode, cancellationToken); var linkedSymbol = semanticModel.GetDeclaredSymbol(linkedNode, cancellationToken);
if (linkedSymbol?.Kind == symbol.Kind && if (linkedSymbol != null &&
linkedSymbol?.Name == symbol.Name) linkedSymbol.Kind == symbol.Kind &&
linkedSymbol.Name == symbol.Name)
{ {
linkedSymbols.Add(linkedSymbol); linkedSymbols.Add(linkedSymbol);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册