提交 12e5e17d 编写于 作者: T Tomáš Matoušek

Improvements to Scripting API exception handling (#11416)

上级 aad4fe55
...@@ -59,13 +59,18 @@ private struct EvaluationState ...@@ -59,13 +59,18 @@ private struct EvaluationState
internal readonly ScriptOptions ScriptOptions; internal readonly ScriptOptions ScriptOptions;
internal EvaluationState( internal EvaluationState(
ScriptState<object> scriptState, ScriptState<object> scriptStateOpt,
ScriptOptions scriptOptions, ScriptOptions scriptOptions,
ImmutableArray<string> sourceSearchPaths, ImmutableArray<string> sourceSearchPaths,
ImmutableArray<string> referenceSearchPaths, ImmutableArray<string> referenceSearchPaths,
string workingDirectory) string workingDirectory)
{ {
ScriptStateOpt = scriptState; Debug.Assert(scriptOptions != null);
Debug.Assert(!sourceSearchPaths.IsDefault);
Debug.Assert(!referenceSearchPaths.IsDefault);
Debug.Assert(workingDirectory != null);
ScriptStateOpt = scriptStateOpt;
ScriptOptions = scriptOptions; ScriptOptions = scriptOptions;
SourceSearchPaths = sourceSearchPaths; SourceSearchPaths = sourceSearchPaths;
ReferenceSearchPaths = referenceSearchPaths; ReferenceSearchPaths = referenceSearchPaths;
...@@ -74,6 +79,8 @@ private struct EvaluationState ...@@ -74,6 +79,8 @@ 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,
...@@ -84,6 +91,8 @@ internal EvaluationState WithScriptState(ScriptState<object> state) ...@@ -84,6 +91,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, ScriptStateOpt,
options, options,
...@@ -104,7 +113,7 @@ internal EvaluationState WithOptions(ScriptOptions options) ...@@ -104,7 +113,7 @@ internal EvaluationState WithOptions(ScriptOptions options)
public Service() public Service()
{ {
var initialState = new EvaluationState( var initialState = new EvaluationState(
scriptState: null, scriptStateOpt: null,
scriptOptions: ScriptOptions.Default, scriptOptions: ScriptOptions.Default,
sourceSearchPaths: ImmutableArray<string>.Empty, sourceSearchPaths: ImmutableArray<string>.Empty,
referenceSearchPaths: ImmutableArray<string>.Empty, referenceSearchPaths: ImmutableArray<string>.Empty,
...@@ -442,12 +451,8 @@ private async Task<EvaluationState> ExecuteAsync(Task<EvaluationState> lastTask, ...@@ -442,12 +451,8 @@ 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 ExecuteOnUIThread(script, state.ScriptStateOpt).ConfigureAwait(false); var newScriptState = await ExecuteOnUIThread(script, state.ScriptStateOpt, displayResult: true).ConfigureAwait(false);
if (newScriptState != null) state = state.WithScriptState(newScriptState);
{
DisplaySubmissionResult(newScriptState);
state = state.WithScriptState(newScriptState);
}
} }
} }
catch (Exception e) catch (Exception e)
...@@ -462,11 +467,15 @@ private async Task<EvaluationState> ExecuteAsync(Task<EvaluationState> lastTask, ...@@ -462,11 +467,15 @@ private async Task<EvaluationState> ExecuteAsync(Task<EvaluationState> lastTask,
return state; return state;
} }
private void DisplaySubmissionResult(ScriptState<object> state) private void DisplayException(Exception e)
{ {
if (state.Script.HasReturnValue()) if (e is FileLoadException && e.InnerException is InteractiveAssemblyLoaderException)
{ {
_globals.Print(state.ReturnValue); Console.Error.WriteLine(e.InnerException.Message);
}
else
{
Console.Error.Write(_replServiceProvider.ObjectFormatter.FormatException(e));
} }
} }
...@@ -633,7 +642,7 @@ private static void ReportUnhandledException(Exception e) ...@@ -633,7 +642,7 @@ private static void ReportUnhandledException(Exception e)
if (scriptPathOpt != null) if (scriptPathOpt != null)
{ {
var newScriptState = await ExecuteFileAsync(rspState, scriptPathOpt).ConfigureAwait(false); var newScriptState = await TryExecuteFileAsync(rspState, scriptPathOpt).ConfigureAwait(false);
if (newScriptState != null) if (newScriptState != null)
{ {
// 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:
...@@ -725,61 +734,53 @@ private Script<object> TryCompile(Script previousScript, string code, string pat ...@@ -725,61 +734,53 @@ private Script<object> TryCompile(Script previousScript, string code, string pat
string path) string path)
{ {
var state = await ReportUnhandledExceptionIfAny(lastTask).ConfigureAwait(false); var state = await ReportUnhandledExceptionIfAny(lastTask).ConfigureAwait(false);
var success = false; string fullPath = ResolveRelativePath(path, state.WorkingDirectory, state.SourceSearchPaths, displayPath: false);
try if (fullPath != null)
{ {
var fullPath = ResolveRelativePath(path, state.WorkingDirectory, state.SourceSearchPaths, displayPath: false); var newScriptState = await TryExecuteFileAsync(state, fullPath).ConfigureAwait(false);
var newScriptState = await ExecuteFileAsync(state, fullPath).ConfigureAwait(false);
if (newScriptState != null) if (newScriptState != null)
{ {
success = true; return CompleteExecution(state.WithScriptState(newScriptState), operation, success: newScriptState.Exception == null);
state = state.WithScriptState(newScriptState);
} }
} }
finally
{
state = CompleteExecution(state, operation, success);
}
return state; return CompleteExecution(state, operation, success: false);
} }
/// <summary> /// <summary>
/// Executes specified script file as a submission. /// Executes specified script file as a submission.
/// </summary> /// </summary>
/// <returns>True if the code has been executed. False if the code doesn't compile.</returns>
/// <remarks> /// <remarks>
/// 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>> ExecuteFileAsync(EvaluationState state, string fullPath) private async Task<ScriptState<object>> TryExecuteFileAsync(EvaluationState state, string fullPath)
{ {
Debug.Assert(PathUtilities.IsAbsolute(fullPath));
string content = null; string content = null;
if (fullPath != null) try
{ {
Debug.Assert(PathUtilities.IsAbsolute(fullPath)); using (var reader = File.OpenText(fullPath))
try
{
content = File.ReadAllText(fullPath);
}
catch (Exception e)
{ {
Console.Error.WriteLine(e.Message); content = await reader.ReadToEndAsync().ConfigureAwait(false);
} }
} }
catch (Exception e)
{
// file read errors:
Console.Error.WriteLine(e.Message);
return null;
}
ScriptState<object> newScriptState = null; Script<object> script = TryCompile(state.ScriptStateOpt?.Script, content, fullPath, state.ScriptOptions);
if (content != null) if (script == null)
{ {
Script<object> script = TryCompile(state.ScriptStateOpt?.Script, content, fullPath, state.ScriptOptions); // compilation errors:
if (script != null) return null;
{
newScriptState = await ExecuteOnUIThread(script, state.ScriptStateOpt).ConfigureAwait(false);
}
} }
return newScriptState; return await ExecuteOnUIThread(script, state.ScriptStateOpt, displayResult: false).ConfigureAwait(false);
} }
private static void DisplaySearchPaths(TextWriter writer, List<string> attemptedFilePaths) private static void DisplaySearchPaths(TextWriter writer, List<string> attemptedFilePaths)
...@@ -801,28 +802,28 @@ private static void DisplaySearchPaths(TextWriter writer, List<string> attempted ...@@ -801,28 +802,28 @@ private static void DisplaySearchPaths(TextWriter writer, List<string> attempted
} }
} }
private async Task<ScriptState<object>> ExecuteOnUIThread(Script<object> script, ScriptState<object> stateOpt) private async Task<ScriptState<object>> ExecuteOnUIThread(Script<object> script, ScriptState<object> stateOpt, bool displayResult)
{ {
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 () =>
{ {
try var task = (stateOpt == null) ?
{ script.RunAsync(_globals, catchException: e => true, cancellationToken: CancellationToken.None) :
var task = (stateOpt == null) ? script.RunFromAsync(stateOpt, catchException: e => true, cancellationToken: CancellationToken.None);
script.RunAsync(_globals, CancellationToken.None) :
script.RunFromAsync(stateOpt, CancellationToken.None); var newState = await task.ConfigureAwait(false);
return await task.ConfigureAwait(false);
} if (newState.Exception != null)
catch (FileLoadException e) when (e.InnerException is InteractiveAssemblyLoaderException)
{ {
Console.Error.WriteLine(e.InnerException.Message); DisplayException(newState.Exception);
return null;
} }
catch (Exception e) else if (displayResult && newState.Script.HasReturnValue())
{ {
Console.Error.Write(_replServiceProvider.ObjectFormatter.FormatException(e)); _globals.Print(newState.ReturnValue);
return null;
} }
return newState;
}))).ConfigureAwait(false); }))).ConfigureAwait(false);
} }
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
...@@ -366,11 +367,10 @@ public void AsyncExecuteFile_SourceKind() ...@@ -366,11 +367,10 @@ public void AsyncExecuteFile_SourceKind()
} }
[Fact] [Fact]
public void AsyncExecuteFile_NonExistingFile() public async Task AsyncExecuteFile_NonExistingFile()
{ {
var task = _host.ExecuteFileAsync("non existing file"); var result = await _host.ExecuteFileAsync("non existing file");
task.Wait(); Assert.False(result.Success);
Assert.False(task.Result.Success);
var errorOut = ReadErrorOutputToEnd().Trim(); var errorOut = ReadErrorOutputToEnd().Trim();
Assert.Contains(FeaturesResources.SpecifiedFileNotFound, errorOut, StringComparison.Ordinal); Assert.Contains(FeaturesResources.SpecifiedFileNotFound, errorOut, StringComparison.Ordinal);
...@@ -1155,6 +1155,20 @@ public void Exception() ...@@ -1155,6 +1155,20 @@ public void Exception()
Assert.True(error.StartsWith(new Exception().Message)); Assert.True(error.StartsWith(new Exception().Message));
} }
[Fact, WorkItem(10883, "https://github.com/dotnet/roslyn/issues/10883")]
public void PreservingDeclarationsOnException()
{
Execute(@"int i = 100;");
Execute(@"int j = 20; throw new System.Exception(""Bang!""); int k = 3;");
Execute(@"i + j + k");
var output = ReadOutputToEnd();
var error = ReadErrorOutputToEnd();
AssertEx.AssertEqualToleratingWhitespaceDifferences("120", output);
AssertEx.AssertEqualToleratingWhitespaceDifferences("Bang!", error);
}
#region Submission result printing - null/void/value. #region Submission result printing - null/void/value.
[Fact] [Fact]
......
...@@ -820,5 +820,35 @@ public class Lib2 ...@@ -820,5 +820,35 @@ public class Lib2
«Gray» «Gray»
> ", runner.Console.Out.ToString()); > ", runner.Console.Out.ToString());
} }
[Fact]
[WorkItem(6580, "https://github.com/dotnet/roslyn/issues/6580")]
public void PreservingDeclarationsOnException()
{
var runner = CreateRunner(input:
@"int i = 100;
int j = 20; throw new System.Exception(""Bang!""); int k = 3;
i + j + k
");
runner.RunInteractive();
AssertEx.AssertEqualToleratingWhitespaceDifferences(
$@"Microsoft (R) Visual C# Interactive Compiler version {s_compilerVersion}
Copyright (C) Microsoft Corporation. All rights reserved.
Type ""#help"" for more information.
> int i = 100;
> int j = 20; throw new System.Exception(""Bang!""); int k = 3;
«Red»
Bang!
«Gray»
> i + j + k
120
> ", runner.Console.Out.ToString());
AssertEx.AssertEqualToleratingWhitespaceDifferences(
@"Bang!",
runner.Console.Error.ToString());
}
} }
} }
...@@ -6,11 +6,12 @@ ...@@ -6,11 +6,12 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;
using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Scripting;
using Microsoft.CodeAnalysis.Scripting.Hosting; using Microsoft.CodeAnalysis.Scripting.Hosting;
using Microsoft.CodeAnalysis.Scripting.Test; using Microsoft.CodeAnalysis.Scripting.Test;
...@@ -2190,5 +2191,217 @@ public void HostObjectAssemblyReference3() ...@@ -2190,5 +2191,217 @@ public void HostObjectAssemblyReference3()
} }
#endregion #endregion
#region Exceptions
[Fact]
[WorkItem(6580, "https://github.com/dotnet/roslyn/issues/6580")]
[WorkItem(10883, "https://github.com/dotnet/roslyn/issues/10883")]
public async Task PreservingDeclarationsOnException1()
{
var s0 = CSharpScript.Create(@"
int i = 10;
throw new System.Exception(""Bang!"");
int j = 2;
");
var s1 = s0.ContinueWith(@"
int F() => i + j;
");
var state1 = await s1.RunAsync(catchException: e => true);
Assert.Equal("Bang!", state1.Exception.Message);
var state2 = await state1.ContinueWithAsync<int>("F()");
Assert.Equal(10, state2.ReturnValue);
}
[Fact]
[WorkItem(6580, "https://github.com/dotnet/roslyn/issues/6580")]
[WorkItem(10883, "https://github.com/dotnet/roslyn/issues/10883")]
public async Task PreservingDeclarationsOnException2()
{
var s0 = CSharpScript.Create(@"
int i = 100;
");
var s1 = s0.ContinueWith(@"
int j = 20;
throw new System.Exception(""Bang!"");
int k = 3;
");
var s2 = s1.ContinueWith(@"
int F() => i + j + k;
");
var state2 = await s2.RunAsync(catchException: e => true);
Assert.Equal("Bang!", state2.Exception.Message);
var state3 = await state2.ContinueWithAsync<int>("F()");
Assert.Equal(120, state3.ReturnValue);
}
[Fact]
[WorkItem(6580, "https://github.com/dotnet/roslyn/issues/6580")]
[WorkItem(10883, "https://github.com/dotnet/roslyn/issues/10883")]
public async Task PreservingDeclarationsOnException3()
{
var s0 = CSharpScript.Create(@"
int i = 1000;
");
var s1 = s0.ContinueWith(@"
int j = 200;
throw new System.Exception(""Bang!"");
int k = 30;
");
var s2 = s1.ContinueWith(@"
int l = 4;
");
var s3 = s2.ContinueWith(@"
int F() => i + j + k + l;
");
var state3 = await s3.RunAsync(catchException: e => true);
Assert.Equal("Bang!", state3.Exception.Message);
var state4 = await state3.ContinueWithAsync<int>("F()");
Assert.Equal(1200, state4.ReturnValue);
}
[Fact]
[WorkItem(6580, "https://github.com/dotnet/roslyn/issues/6580")]
[WorkItem(10883, "https://github.com/dotnet/roslyn/issues/10883")]
public async Task PreservingDeclarationsOnException4()
{
var state0 = await CSharpScript.RunAsync(@"
int i = 1000;
");
var state1 = await state0.ContinueWithAsync(@"
int j = 200;
throw new System.Exception(""Bang 1!"");
int k = 30;
", catchException: e => true);
Assert.Equal("Bang 1!", state1.Exception.Message);
var state2 = await state1.ContinueWithAsync<int>(@"
int l = 4;
throw new System.Exception(""Bang 2!"");
1
", catchException: e => true);
Assert.Equal("Bang 2!", state2.Exception.Message);
var state4 = await state2.ContinueWithAsync(@"
i + j + k + l
");
Assert.Equal(1204, state4.ReturnValue);
}
[Fact]
public async Task PreservingDeclarationsOnCancellation1()
{
var cancellationSource = new CancellationTokenSource();
var globals = new StrongBox<CancellationTokenSource>();
globals.Value = cancellationSource;
var s0 = CSharpScript.Create(@"
int i = 1000;
", globalsType: globals.GetType());
var s1 = s0.ContinueWith(@"
int j = 200;
Value.Cancel();
int k = 30;
");
// cancellation exception is thrown just before we start evaluating s2:
var s2 = s1.ContinueWith(@"
int l = 4;
");
var s3 = s2.ContinueWith(@"
int F() => i + j + k + l;
");
var state3 = await s3.RunAsync(globals, catchException: e => true, cancellationToken: cancellationSource.Token);
Assert.IsType<OperationCanceledException>(state3.Exception);
var state4 = await state3.ContinueWithAsync<int>("F()");
Assert.Equal(1230, state4.ReturnValue);
}
[Fact]
public async Task PreservingDeclarationsOnCancellation2()
{
var cancellationSource = new CancellationTokenSource();
var globals = new StrongBox<CancellationTokenSource>();
globals.Value = cancellationSource;
var s0 = CSharpScript.Create(@"
int i = 1000;
", globalsType: globals.GetType());
var s1 = s0.ContinueWith(@"
int j = 200;
int k = 30;
");
var s2 = s1.ContinueWith(@"
int l = 4;
Value.Cancel();
");
// cancellation exception is thrown just before we start evaluating s3:
var s3 = s2.ContinueWith(@"
int F() => i + j + k + l;
");
var state3 = await s3.RunAsync(globals, catchException: e => true, cancellationToken: cancellationSource.Token);
Assert.IsType<OperationCanceledException>(state3.Exception);
var state4 = await state3.ContinueWithAsync<int>("F()");
Assert.Equal(1234, state4.ReturnValue);
}
[Fact]
public async Task PreservingDeclarationsOnCancellation3()
{
var cancellationSource = new CancellationTokenSource();
var globals = new StrongBox<CancellationTokenSource>();
globals.Value = cancellationSource;
var s0 = CSharpScript.Create(@"
int i = 1000;
", globalsType: globals.GetType());
var s1 = s0.ContinueWith(@"
int j = 200;
Value.Cancel();
int k = 30;
");
// cancellation exception is thrown just before we start evaluating s2:
var s2 = s1.ContinueWith(@"
int l = 4;
");
var s3 = s2.ContinueWith(@"
int F() => i + j + k + l;
");
await Assert.ThrowsAsync<OperationCanceledException>(() =>
s3.RunAsync(globals, catchException: e => !(e is OperationCanceledException), cancellationToken: cancellationSource.Token));
}
#endregion
} }
} }
...@@ -191,7 +191,7 @@ private void RunInteractiveLoop(ScriptOptions options, string initialScriptCodeO ...@@ -191,7 +191,7 @@ private void RunInteractiveLoop(ScriptOptions options, string initialScriptCodeO
if (initialScriptCodeOpt != null) if (initialScriptCodeOpt != null)
{ {
var script = Script.CreateInitialScript<object>(_scriptCompiler, initialScriptCodeOpt, options, globals.GetType(), assemblyLoaderOpt: null); var script = Script.CreateInitialScript<object>(_scriptCompiler, initialScriptCodeOpt, options, globals.GetType(), assemblyLoaderOpt: null);
TryBuildAndRun(script, globals, ref state, ref options, cancellationToken); BuildAndRun(script, globals, ref state, ref options, displayResult: false, cancellationToken: cancellationToken);
} }
while (true) while (true)
...@@ -249,52 +249,34 @@ private void RunInteractiveLoop(ScriptOptions options, string initialScriptCodeO ...@@ -249,52 +249,34 @@ private void RunInteractiveLoop(ScriptOptions options, string initialScriptCodeO
newScript = state.Script.ContinueWith(code, options); newScript = state.Script.ContinueWith(code, options);
} }
if (!TryBuildAndRun(newScript, globals, ref state, ref options, cancellationToken)) BuildAndRun(newScript, globals, ref state, ref options, displayResult: true, cancellationToken: cancellationToken);
{
continue;
}
if (newScript.HasReturnValue())
{
globals.Print(state.ReturnValue);
}
} }
} }
private bool TryBuildAndRun(Script<object> newScript, InteractiveScriptGlobals globals, ref ScriptState<object> state, ref ScriptOptions options, CancellationToken cancellationToken) private void BuildAndRun(Script<object> newScript, InteractiveScriptGlobals globals, ref ScriptState<object> state, ref ScriptOptions options, bool displayResult, CancellationToken cancellationToken)
{ {
var diagnostics = newScript.Compile(cancellationToken); var diagnostics = newScript.Compile(cancellationToken);
DisplayDiagnostics(diagnostics); DisplayDiagnostics(diagnostics);
if (diagnostics.HasAnyErrors()) if (diagnostics.HasAnyErrors())
{ {
return false; return;
} }
try var task = (state == null) ?
{ newScript.RunAsync(globals, catchException: e => true, cancellationToken: cancellationToken) :
var task = (state == null) ? newScript.RunFromAsync(state, catchException: e => true, cancellationToken: cancellationToken);
newScript.RunAsync(globals, cancellationToken) :
newScript.RunFromAsync(state, cancellationToken);
state = task.GetAwaiter().GetResult(); state = task.GetAwaiter().GetResult();
} if (state.Exception != null)
catch (FileLoadException e) when (e.InnerException is InteractiveAssemblyLoaderException)
{ {
_console.ForegroundColor = ConsoleColor.Red; DisplayException(state.Exception);
_console.Error.WriteLine(e.InnerException.Message);
_console.ResetColor();
return false;
} }
catch (Exception e) else if (displayResult && newScript.HasReturnValue())
{ {
DisplayException(e); globals.Print(state.ReturnValue);
return false;
} }
options = UpdateOptions(options, globals); options = UpdateOptions(options, globals);
return true;
} }
private static ScriptOptions UpdateOptions(ScriptOptions options, InteractiveScriptGlobals globals) private static ScriptOptions UpdateOptions(ScriptOptions options, InteractiveScriptGlobals globals)
...@@ -324,7 +306,15 @@ private void DisplayException(Exception e) ...@@ -324,7 +306,15 @@ private void DisplayException(Exception e)
try try
{ {
_console.ForegroundColor = ConsoleColor.Red; _console.ForegroundColor = ConsoleColor.Red;
_console.Error.Write(_objectFormatter.FormatException(e));
if (e is FileLoadException && e.InnerException is InteractiveAssemblyLoaderException)
{
_console.Error.WriteLine(e.InnerException.Message);
}
else
{
_console.Error.Write(_objectFormatter.FormatException(e));
}
} }
finally finally
{ {
......
...@@ -59,13 +59,9 @@ Microsoft.CodeAnalysis.Scripting.Script.GetCompilation() -> Microsoft.CodeAnalys ...@@ -59,13 +59,9 @@ Microsoft.CodeAnalysis.Scripting.Script.GetCompilation() -> Microsoft.CodeAnalys
Microsoft.CodeAnalysis.Scripting.Script.GlobalsType.get -> System.Type Microsoft.CodeAnalysis.Scripting.Script.GlobalsType.get -> System.Type
Microsoft.CodeAnalysis.Scripting.Script.Options.get -> Microsoft.CodeAnalysis.Scripting.ScriptOptions Microsoft.CodeAnalysis.Scripting.Script.Options.get -> Microsoft.CodeAnalysis.Scripting.ScriptOptions
Microsoft.CodeAnalysis.Scripting.Script.Previous.get -> Microsoft.CodeAnalysis.Scripting.Script Microsoft.CodeAnalysis.Scripting.Script.Previous.get -> Microsoft.CodeAnalysis.Scripting.Script
Microsoft.CodeAnalysis.Scripting.Script.RunAsync(object globals = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState>
Microsoft.CodeAnalysis.Scripting.Script.RunFromAsync(Microsoft.CodeAnalysis.Scripting.ScriptState previousState, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState>
Microsoft.CodeAnalysis.Scripting.Script.WithOptions(Microsoft.CodeAnalysis.Scripting.ScriptOptions options) -> Microsoft.CodeAnalysis.Scripting.Script Microsoft.CodeAnalysis.Scripting.Script.WithOptions(Microsoft.CodeAnalysis.Scripting.ScriptOptions options) -> Microsoft.CodeAnalysis.Scripting.Script
Microsoft.CodeAnalysis.Scripting.Script<T> Microsoft.CodeAnalysis.Scripting.Script<T>
Microsoft.CodeAnalysis.Scripting.Script<T>.CreateDelegate(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Scripting.ScriptRunner<T> Microsoft.CodeAnalysis.Scripting.Script<T>.CreateDelegate(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Scripting.ScriptRunner<T>
Microsoft.CodeAnalysis.Scripting.Script<T>.RunAsync(object globals = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState<T>>
Microsoft.CodeAnalysis.Scripting.Script<T>.RunFromAsync(Microsoft.CodeAnalysis.Scripting.ScriptState previousState, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState<T>>
Microsoft.CodeAnalysis.Scripting.Script<T>.WithOptions(Microsoft.CodeAnalysis.Scripting.ScriptOptions options) -> Microsoft.CodeAnalysis.Scripting.Script<T> Microsoft.CodeAnalysis.Scripting.Script<T>.WithOptions(Microsoft.CodeAnalysis.Scripting.ScriptOptions options) -> Microsoft.CodeAnalysis.Scripting.Script<T>
Microsoft.CodeAnalysis.Scripting.ScriptMetadataResolver Microsoft.CodeAnalysis.Scripting.ScriptMetadataResolver
Microsoft.CodeAnalysis.Scripting.ScriptMetadataResolver.BaseDirectory.get -> string Microsoft.CodeAnalysis.Scripting.ScriptMetadataResolver.BaseDirectory.get -> string
...@@ -108,8 +104,6 @@ Microsoft.CodeAnalysis.Scripting.ScriptSourceResolver.WithSearchPaths(System.Col ...@@ -108,8 +104,6 @@ Microsoft.CodeAnalysis.Scripting.ScriptSourceResolver.WithSearchPaths(System.Col
Microsoft.CodeAnalysis.Scripting.ScriptSourceResolver.WithSearchPaths(System.Collections.Immutable.ImmutableArray<string> searchPaths) -> Microsoft.CodeAnalysis.Scripting.ScriptSourceResolver Microsoft.CodeAnalysis.Scripting.ScriptSourceResolver.WithSearchPaths(System.Collections.Immutable.ImmutableArray<string> searchPaths) -> Microsoft.CodeAnalysis.Scripting.ScriptSourceResolver
Microsoft.CodeAnalysis.Scripting.ScriptSourceResolver.WithSearchPaths(params string[] searchPaths) -> Microsoft.CodeAnalysis.Scripting.ScriptSourceResolver Microsoft.CodeAnalysis.Scripting.ScriptSourceResolver.WithSearchPaths(params string[] searchPaths) -> Microsoft.CodeAnalysis.Scripting.ScriptSourceResolver
Microsoft.CodeAnalysis.Scripting.ScriptState Microsoft.CodeAnalysis.Scripting.ScriptState
Microsoft.CodeAnalysis.Scripting.ScriptState.ContinueWithAsync(string code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState<object>>
Microsoft.CodeAnalysis.Scripting.ScriptState.ContinueWithAsync<TResult>(string code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState<TResult>>
Microsoft.CodeAnalysis.Scripting.ScriptState.GetVariable(string name) -> Microsoft.CodeAnalysis.Scripting.ScriptVariable Microsoft.CodeAnalysis.Scripting.ScriptState.GetVariable(string name) -> Microsoft.CodeAnalysis.Scripting.ScriptVariable
Microsoft.CodeAnalysis.Scripting.ScriptState.ReturnValue.get -> object Microsoft.CodeAnalysis.Scripting.ScriptState.ReturnValue.get -> object
Microsoft.CodeAnalysis.Scripting.ScriptState.Script.get -> Microsoft.CodeAnalysis.Scripting.Script Microsoft.CodeAnalysis.Scripting.ScriptState.Script.get -> Microsoft.CodeAnalysis.Scripting.Script
......
Microsoft.CodeAnalysis.Scripting.Script.RunAsync(object globals = null, System.Func<System.Exception, bool> catchException = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState>
Microsoft.CodeAnalysis.Scripting.Script.RunAsync(object globals, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState>
Microsoft.CodeAnalysis.Scripting.Script.RunFromAsync(Microsoft.CodeAnalysis.Scripting.ScriptState previousState, System.Func<System.Exception, bool> catchException = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState>
Microsoft.CodeAnalysis.Scripting.Script.RunFromAsync(Microsoft.CodeAnalysis.Scripting.ScriptState previousState, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState>
Microsoft.CodeAnalysis.Scripting.Script<T>.RunAsync(object globals = null, System.Func<System.Exception, bool> catchException = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState<T>>
Microsoft.CodeAnalysis.Scripting.Script<T>.RunAsync(object globals, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState<T>>
Microsoft.CodeAnalysis.Scripting.Script<T>.RunFromAsync(Microsoft.CodeAnalysis.Scripting.ScriptState previousState, System.Func<System.Exception, bool> catchException = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState<T>>
Microsoft.CodeAnalysis.Scripting.Script<T>.RunFromAsync(Microsoft.CodeAnalysis.Scripting.ScriptState previousState, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState<T>>
Microsoft.CodeAnalysis.Scripting.ScriptState.ContinueWithAsync(string code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null, System.Func<System.Exception, bool> catchException = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState<object>>
Microsoft.CodeAnalysis.Scripting.ScriptState.ContinueWithAsync(string code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState<object>>
Microsoft.CodeAnalysis.Scripting.ScriptState.ContinueWithAsync<TResult>(string code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null, System.Func<System.Exception, bool> catchException = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState<TResult>>
Microsoft.CodeAnalysis.Scripting.ScriptState.ContinueWithAsync<TResult>(string code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Scripting.ScriptState<TResult>>
Microsoft.CodeAnalysis.Scripting.ScriptState.Exception.get -> System.Exception
\ No newline at end of file
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Roslyn.Utilities; using Roslyn.Utilities;
using Microsoft.CodeAnalysis.Scripting.Hosting; using Microsoft.CodeAnalysis.Scripting.Hosting;
using System.Runtime.CompilerServices;
namespace Microsoft.CodeAnalysis.Scripting namespace Microsoft.CodeAnalysis.Scripting
{ {
...@@ -136,10 +137,26 @@ public Compilation GetCompilation() ...@@ -136,10 +137,26 @@ public Compilation GetCompilation()
/// </param> /// </param>
/// <param name="cancellationToken">Cancellation token.</param> /// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="ScriptState"/> that represents the state after running the script, including all declared variables and return value.</returns> /// <returns>A <see cref="ScriptState"/> that represents the state after running the script, including all declared variables and return value.</returns>
public Task<ScriptState> RunAsync(object globals = null, CancellationToken cancellationToken = default(CancellationToken)) => public Task<ScriptState> RunAsync(object globals, CancellationToken cancellationToken) =>
CommonRunAsync(globals, cancellationToken); CommonRunAsync(globals, null, cancellationToken);
internal abstract Task<ScriptState> CommonRunAsync(object globals, CancellationToken cancellationToken); /// <summary>
/// Runs the script from the beginning.
/// </summary>
/// <param name="globals">
/// An instance of <see cref="Script.GlobalsType"/> holding on values for global variables accessible from the script.
/// Must be specified if and only if the script was created with <see cref="Script.GlobalsType"/>.
/// </param>
/// <param name="catchException">
/// If specified, any exception thrown by the script top-level code is passed to <paramref name="catchException"/>.
/// If it returns true the exception is caught and stored on the resulting <see cref="ScriptState"/>, otherwise the exception is propagated to the caller.
/// </param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="ScriptState"/> that represents the state after running the script, including all declared variables and return value.</returns>
public Task<ScriptState> RunAsync(object globals = null, Func<Exception, bool> catchException = null, CancellationToken cancellationToken = default(CancellationToken)) =>
CommonRunAsync(globals, catchException, cancellationToken);
internal abstract Task<ScriptState> CommonRunAsync(object globals, Func<Exception, bool> catchException, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Run the script from the specified state. /// Run the script from the specified state.
...@@ -149,10 +166,25 @@ public Compilation GetCompilation() ...@@ -149,10 +166,25 @@ public Compilation GetCompilation()
/// </param> /// </param>
/// <param name="cancellationToken">Cancellation token.</param> /// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="ScriptState"/> that represents the state after running the script, including all declared variables and return value.</returns> /// <returns>A <see cref="ScriptState"/> that represents the state after running the script, including all declared variables and return value.</returns>
public Task<ScriptState> RunFromAsync(ScriptState previousState, CancellationToken cancellationToken = default(CancellationToken)) => public Task<ScriptState> RunFromAsync(ScriptState previousState, CancellationToken cancellationToken) =>
CommonRunFromAsync(previousState, cancellationToken); CommonRunFromAsync(previousState, null, cancellationToken);
internal abstract Task<ScriptState> CommonRunFromAsync(ScriptState previousState, CancellationToken cancellationToken); /// <summary>
/// Run the script from the specified state.
/// </summary>
/// <param name="previousState">
/// Previous state of the script execution.
/// </param>
/// <param name="catchException">
/// If specified, any exception thrown by the script top-level code is passed to <paramref name="catchException"/>.
/// If it returns true the exception is caught and stored on the resulting <see cref="ScriptState"/>, otherwise the exception is propagated to the caller.
/// </param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="ScriptState"/> that represents the state after running the script, including all declared variables and return value.</returns>
public Task<ScriptState> RunFromAsync(ScriptState previousState, Func<Exception, bool> catchException = null, CancellationToken cancellationToken = default(CancellationToken)) =>
CommonRunFromAsync(previousState, catchException, cancellationToken);
internal abstract Task<ScriptState> CommonRunFromAsync(ScriptState previousState, Func<Exception, bool> catchException, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Forces the script through the compilation step. /// Forces the script through the compilation step.
...@@ -282,11 +314,11 @@ internal override ImmutableArray<Diagnostic> CommonCompile(CancellationToken can ...@@ -282,11 +314,11 @@ internal override ImmutableArray<Diagnostic> CommonCompile(CancellationToken can
internal override Task<object> CommonEvaluateAsync(object globals, CancellationToken cancellationToken) => internal override Task<object> CommonEvaluateAsync(object globals, CancellationToken cancellationToken) =>
EvaluateAsync(globals, cancellationToken).CastAsync<T, object>(); EvaluateAsync(globals, cancellationToken).CastAsync<T, object>();
internal override Task<ScriptState> CommonRunAsync(object globals, CancellationToken cancellationToken) => internal override Task<ScriptState> CommonRunAsync(object globals, Func<Exception, bool> catchException, CancellationToken cancellationToken) =>
RunAsync(globals, cancellationToken).CastAsync<ScriptState<T>, ScriptState>(); RunAsync(globals, catchException, cancellationToken).CastAsync<ScriptState<T>, ScriptState>();
internal override Task<ScriptState> CommonRunFromAsync(ScriptState previousState, CancellationToken cancellationToken) => internal override Task<ScriptState> CommonRunFromAsync(ScriptState previousState, Func<Exception, bool> catchException, CancellationToken cancellationToken) =>
RunFromAsync(previousState, cancellationToken).CastAsync<ScriptState<T>, ScriptState>(); RunFromAsync(previousState, catchException, cancellationToken).CastAsync<ScriptState<T>, ScriptState>();
/// <exception cref="CompilationErrorException">Compilation has errors.</exception> /// <exception cref="CompilationErrorException">Compilation has errors.</exception>
private Func<object[], Task<T>> GetExecutor(CancellationToken cancellationToken) private Func<object[], Task<T>> GetExecutor(CancellationToken cancellationToken)
...@@ -370,7 +402,25 @@ internal override ImmutableArray<Diagnostic> CommonCompile(CancellationToken can ...@@ -370,7 +402,25 @@ internal override ImmutableArray<Diagnostic> CommonCompile(CancellationToken can
/// <returns>A <see cref="ScriptState"/> that represents the state after running the script, including all declared variables and return value.</returns> /// <returns>A <see cref="ScriptState"/> that represents the state after running the script, including all declared variables and return value.</returns>
/// <exception cref="CompilationErrorException">Compilation has errors.</exception> /// <exception cref="CompilationErrorException">Compilation has errors.</exception>
/// <exception cref="ArgumentException">The type of <paramref name="globals"/> doesn't match <see cref="Script.GlobalsType"/>.</exception> /// <exception cref="ArgumentException">The type of <paramref name="globals"/> doesn't match <see cref="Script.GlobalsType"/>.</exception>
public new Task<ScriptState<T>> RunAsync(object globals = null, CancellationToken cancellationToken = default(CancellationToken)) public new Task<ScriptState<T>> RunAsync(object globals, CancellationToken cancellationToken)
=> RunAsync(globals, null, cancellationToken);
/// <summary>
/// Runs the script from the beginning.
/// </summary>
/// <param name="globals">
/// An instance of <see cref="Script.GlobalsType"/> holding on values for global variables accessible from the script.
/// Must be specified if and only if the script was created with <see cref="Script.GlobalsType"/>.
/// </param>
/// <param name="catchException">
/// If specified, any exception thrown by the script top-level code is passed to <paramref name="catchException"/>.
/// If it returns true the exception is caught and stored on the resulting <see cref="ScriptState"/>, otherwise the exception is propagated to the caller.
/// </param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="ScriptState"/> that represents the state after running the script, including all declared variables and return value.</returns>
/// <exception cref="CompilationErrorException">Compilation has errors.</exception>
/// <exception cref="ArgumentException">The type of <paramref name="globals"/> doesn't match <see cref="Script.GlobalsType"/>.</exception>
public new Task<ScriptState<T>> RunAsync(object globals = null, Func<Exception, bool> catchException = null, CancellationToken cancellationToken = default(CancellationToken))
{ {
// The following validation and executor construction may throw; // The following validation and executor construction may throw;
// do so synchronously so that the exception is not wrapped in the task. // do so synchronously so that the exception is not wrapped in the task.
...@@ -381,7 +431,7 @@ public new Task<ScriptState<T>> RunAsync(object globals = null, CancellationToke ...@@ -381,7 +431,7 @@ public new Task<ScriptState<T>> RunAsync(object globals = null, CancellationToke
var precedingExecutors = GetPrecedingExecutors(cancellationToken); var precedingExecutors = GetPrecedingExecutors(cancellationToken);
var currentExecutor = GetExecutor(cancellationToken); var currentExecutor = GetExecutor(cancellationToken);
return RunSubmissionsAsync(executionState, precedingExecutors, currentExecutor, cancellationToken); return RunSubmissionsAsync(executionState, precedingExecutors, currentExecutor, catchException, cancellationToken);
} }
/// <summary> /// <summary>
...@@ -399,7 +449,7 @@ public ScriptRunner<T> CreateDelegate(CancellationToken cancellationToken = defa ...@@ -399,7 +449,7 @@ public ScriptRunner<T> CreateDelegate(CancellationToken cancellationToken = defa
return (globals, token) => return (globals, token) =>
{ {
ValidateGlobals(globals, globalsType); ValidateGlobals(globals, globalsType);
return ScriptExecutionState.Create(globals).RunSubmissionsAsync<T>(precedingExecutors, currentExecutor, token); return ScriptExecutionState.Create(globals).RunSubmissionsAsync<T>(precedingExecutors, currentExecutor, null, null, token);
}; };
} }
...@@ -413,7 +463,24 @@ public ScriptRunner<T> CreateDelegate(CancellationToken cancellationToken = defa ...@@ -413,7 +463,24 @@ public ScriptRunner<T> CreateDelegate(CancellationToken cancellationToken = defa
/// <returns>A <see cref="ScriptState"/> that represents the state after running the script, including all declared variables and return value.</returns> /// <returns>A <see cref="ScriptState"/> that represents the state after running the script, including all declared variables and return value.</returns>
/// <exception cref="ArgumentNullException"><paramref name="previousState"/> is null.</exception> /// <exception cref="ArgumentNullException"><paramref name="previousState"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="previousState"/> is not a previous execution state of this script.</exception> /// <exception cref="ArgumentException"><paramref name="previousState"/> is not a previous execution state of this script.</exception>
public new Task<ScriptState<T>> RunFromAsync(ScriptState previousState, CancellationToken cancellationToken = default(CancellationToken)) public new Task<ScriptState<T>> RunFromAsync(ScriptState previousState, CancellationToken cancellationToken)
=> RunFromAsync(previousState, null, cancellationToken);
/// <summary>
/// Run the script from the specified state.
/// </summary>
/// <param name="previousState">
/// Previous state of the script execution.
/// </param>
/// <param name="catchException">
/// If specified, any exception thrown by the script top-level code is passed to <paramref name="catchException"/>.
/// If it returns true the exception is caught and stored on the resulting <see cref="ScriptState"/>, otherwise the exception is propagated to the caller.
/// </param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="ScriptState"/> that represents the state after running the script, including all declared variables and return value.</returns>
/// <exception cref="ArgumentNullException"><paramref name="previousState"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="previousState"/> is not a previous execution state of this script.</exception>
public new Task<ScriptState<T>> RunFromAsync(ScriptState previousState, Func<Exception, bool> catchException = null, CancellationToken cancellationToken = default(CancellationToken))
{ {
// The following validation and executor construction may throw; // The following validation and executor construction may throw;
// do so synchronously so that the exception is not wrapped in the task. // do so synchronously so that the exception is not wrapped in the task.
...@@ -438,13 +505,19 @@ public new Task<ScriptState<T>> RunFromAsync(ScriptState previousState, Cancella ...@@ -438,13 +505,19 @@ public new Task<ScriptState<T>> RunFromAsync(ScriptState previousState, Cancella
var currentExecutor = GetExecutor(cancellationToken); var currentExecutor = GetExecutor(cancellationToken);
ScriptExecutionState newExecutionState = previousState.ExecutionState.FreezeAndClone(); ScriptExecutionState newExecutionState = previousState.ExecutionState.FreezeAndClone();
return RunSubmissionsAsync(newExecutionState, precedingExecutors, currentExecutor, cancellationToken); return RunSubmissionsAsync(newExecutionState, precedingExecutors, currentExecutor, catchException, cancellationToken);
} }
private async Task<ScriptState<T>> RunSubmissionsAsync(ScriptExecutionState executionState, ImmutableArray<Func<object[], Task>> precedingExecutors, Func<object[], Task> currentExecutor, CancellationToken cancellationToken) private async Task<ScriptState<T>> RunSubmissionsAsync(
ScriptExecutionState executionState,
ImmutableArray<Func<object[], Task>> precedingExecutors,
Func<object[], Task> currentExecutor,
Func<Exception, bool> catchExceptionOpt,
CancellationToken cancellationToken)
{ {
var result = await executionState.RunSubmissionsAsync<T>(precedingExecutors, currentExecutor, cancellationToken).ConfigureAwait(continueOnCapturedContext: false); var exceptionOpt = (catchExceptionOpt != null) ? new StrongBox<Exception>() : null;
return new ScriptState<T>(executionState, result, this); T result = await executionState.RunSubmissionsAsync<T>(precedingExecutors, currentExecutor, exceptionOpt, catchExceptionOpt, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
return new ScriptState<T>(executionState, this, result, exceptionOpt?.Value);
} }
private static void ValidateGlobals(object globals, Type globalsType) private static void ValidateGlobals(object globals, Type globalsType)
......
...@@ -3,8 +3,11 @@ ...@@ -3,8 +3,11 @@
using System; using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Scripting namespace Microsoft.CodeAnalysis.Scripting
{ {
...@@ -62,26 +65,90 @@ public object GetSubmissionState(int index) ...@@ -62,26 +65,90 @@ public object GetSubmissionState(int index)
internal async Task<TResult> RunSubmissionsAsync<TResult>( internal async Task<TResult> RunSubmissionsAsync<TResult>(
ImmutableArray<Func<object[], Task>> precedingExecutors, ImmutableArray<Func<object[], Task>> precedingExecutors,
Func<object[], Task> currentExecutor, Func<object[], Task> currentExecutor,
StrongBox<Exception> exceptionHolderOpt,
Func<Exception, bool> catchExceptionOpt,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
Debug.Assert(_frozen == 0); Debug.Assert(_frozen == 0);
Debug.Assert((exceptionHolderOpt != null) == (catchExceptionOpt != null));
foreach (var executor in precedingExecutors) // Each executor points to a <Factory> method of the Submission class.
// The method creates an instance of the Submission class passing the submission states to its constructor.
// The consturctor initializes the links between submissions and stores the Submission instance to
// a slot in submission states that corresponds to the submission.
// The <Factory> method then calls the <Initialize> method that consists of top-level script code statements.
int executorIndex = 0;
try
{ {
while (executorIndex < precedingExecutors.Length)
{
cancellationToken.ThrowIfCancellationRequested();
EnsureStateCapacity();
try
{
await precedingExecutors[executorIndex++](_submissionStates).ConfigureAwait(continueOnCapturedContext: false);
}
finally
{
// The submission constructor always runs into completion (unless we emitted bad code).
// We need to advance the counter to reflect the updates to submission states done in the constructor.
AdvanceStateCounter();
}
}
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
TResult result;
EnsureStateCapacity(); EnsureStateCapacity();
await executor(_submissionStates).ConfigureAwait(continueOnCapturedContext: false);
AdvanceStateCounter(); try
{
executorIndex++;
result = await ((Task<TResult>)currentExecutor(_submissionStates)).ConfigureAwait(continueOnCapturedContext: false);
}
finally
{
// The submission constructor always runs into completion (unless we emitted bad code).
// We need to advance the counter to reflect the updates to submission states done in the constructor.
AdvanceStateCounter();
}
return result;
} }
catch (Exception exception) when (catchExceptionOpt?.Invoke(exception) == true)
{
// The following code creates instances of all submissions without executing the user code.
// The constructors don't contain any user code.
var submissionCtorArgs = new object[] { null };
while (executorIndex < precedingExecutors.Length)
{
EnsureStateCapacity();
cancellationToken.ThrowIfCancellationRequested(); // update the value since the array might have been resized:
submissionCtorArgs[0] = _submissionStates;
EnsureStateCapacity(); Activator.CreateInstance(precedingExecutors[executorIndex++].GetMethodInfo().DeclaringType, submissionCtorArgs);
TResult result = await ((Task<TResult>)currentExecutor(_submissionStates)).ConfigureAwait(continueOnCapturedContext: false); AdvanceStateCounter();
AdvanceStateCounter(); }
return result; if (executorIndex == precedingExecutors.Length)
{
EnsureStateCapacity();
// update the value since the array might have been resized:
submissionCtorArgs[0] = _submissionStates;
Activator.CreateInstance(currentExecutor.GetMethodInfo().DeclaringType, submissionCtorArgs);
AdvanceStateCounter();
}
exceptionHolderOpt.Value = exception;
return default(TResult);
}
} }
private void EnsureStateCapacity() private void EnsureStateCapacity()
......
...@@ -20,18 +20,28 @@ public abstract class ScriptState ...@@ -20,18 +20,28 @@ public abstract class ScriptState
/// </summary> /// </summary>
public Script Script { get; } public Script Script { get; }
/// <summary>
/// Caught exception originating from the script top-level code.
/// </summary>
/// <remarks>
/// Exceptions are only caught and stored here if the API returning the <see cref="ScriptState"/> is instructed to do so.
/// By default they are propagated to the caller of the API.
/// </remarks>
public Exception Exception { get; }
internal ScriptExecutionState ExecutionState { get; } internal ScriptExecutionState ExecutionState { get; }
private ImmutableArray<ScriptVariable> _lazyVariables; private ImmutableArray<ScriptVariable> _lazyVariables;
private IReadOnlyDictionary<string, int> _lazyVariableMap; private IReadOnlyDictionary<string, int> _lazyVariableMap;
internal ScriptState(ScriptExecutionState executionState, Script script) internal ScriptState(ScriptExecutionState executionState, Script script, Exception exceptionOpt)
{ {
Debug.Assert(executionState != null); Debug.Assert(executionState != null);
Debug.Assert(script != null); Debug.Assert(script != null);
ExecutionState = executionState; ExecutionState = executionState;
Script = script; Script = script;
Exception = exceptionOpt;
} }
/// <summary> /// <summary>
...@@ -39,7 +49,7 @@ internal ScriptState(ScriptExecutionState executionState, Script script) ...@@ -39,7 +49,7 @@ internal ScriptState(ScriptExecutionState executionState, Script script)
/// </summary> /// </summary>
public object ReturnValue => GetReturnValue(); public object ReturnValue => GetReturnValue();
internal abstract object GetReturnValue(); internal abstract object GetReturnValue();
/// <summary> /// <summary>
/// Returns variables defined by the scripts in the declaration order. /// Returns variables defined by the scripts in the declaration order.
/// </summary> /// </summary>
...@@ -117,15 +127,53 @@ private ImmutableArray<ScriptVariable> CreateVariables() ...@@ -117,15 +127,53 @@ private ImmutableArray<ScriptVariable> CreateVariables()
return _lazyVariableMap; return _lazyVariableMap;
} }
public Task<ScriptState<object>> ContinueWithAsync(string code, ScriptOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) /// <summary>
{ /// Continues script execution from the state represented by this instance by running the specified code snippet.
return ContinueWithAsync<object>(code, options, cancellationToken); /// </summary>
} /// <param name="code">The code to be executed.</param>
/// <param name="options">Options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="ScriptState"/> that represents the state after running <paramref name="code"/>, including all declared variables and return value.</returns>
public Task<ScriptState<object>> ContinueWithAsync(string code, ScriptOptions options, CancellationToken cancellationToken)
=> ContinueWithAsync<object>(code, options, null, cancellationToken);
public Task<ScriptState<TResult>> ContinueWithAsync<TResult>(string code, ScriptOptions options = null, CancellationToken cancellationToken = default(CancellationToken)) /// <summary>
{ /// Continues script execution from the state represented by this instance by running the specified code snippet.
return Script.ContinueWith<TResult>(code, options).RunFromAsync(this, cancellationToken); /// </summary>
} /// <param name="code">The code to be executed.</param>
/// <param name="options">Options.</param>
/// <param name="catchException">
/// If specified, any exception thrown by the script top-level code is passed to <paramref name="catchException"/>.
/// If it returns true the exception is caught and stored on the resulting <see cref="ScriptState"/>, otherwise the exception is propagated to the caller.
/// </param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="ScriptState"/> that represents the state after running <paramref name="code"/>, including all declared variables, return value and caught exception (if applicable).</returns>
public Task<ScriptState<object>> ContinueWithAsync(string code, ScriptOptions options = null, Func<Exception, bool> catchException = null, CancellationToken cancellationToken = default(CancellationToken))
=> Script.ContinueWith<object>(code, options).RunFromAsync(this, catchException, cancellationToken);
/// <summary>
/// Continues script execution from the state represented by this instance by running the specified code snippet.
/// </summary>
/// <param name="code">The code to be executed.</param>
/// <param name="options">Options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="ScriptState"/> that represents the state after running <paramref name="code"/>, including all declared variables and return value.</returns>
public Task<ScriptState<TResult>> ContinueWithAsync<TResult>(string code, ScriptOptions options, CancellationToken cancellationToken)
=> ContinueWithAsync<TResult>(code, options, null, cancellationToken);
/// <summary>
/// Continues script execution from the state represented by this instance by running the specified code snippet.
/// </summary>
/// <param name="code">The code to be executed.</param>
/// <param name="options">Options.</param>
/// <param name="catchException">
/// If specified, any exception thrown by the script top-level code is passed to <paramref name="catchException"/>.
/// If it returns true the exception is caught and stored on the resulting <see cref="ScriptState"/>, otherwise the exception is propagated to the caller.
/// </param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="ScriptState"/> that represents the state after running <paramref name="code"/>, including all declared variables, return value and caught exception (if applicable).</returns>
public Task<ScriptState<TResult>> ContinueWithAsync<TResult>(string code, ScriptOptions options = null, Func<Exception, bool> catchException = null, CancellationToken cancellationToken = default(CancellationToken))
=> Script.ContinueWith<TResult>(code, options).RunFromAsync(this, catchException, cancellationToken);
// How do we resolve overloads? We should use the language semantics. // How do we resolve overloads? We should use the language semantics.
// https://github.com/dotnet/roslyn/issues/3720 // https://github.com/dotnet/roslyn/issues/3720
...@@ -219,8 +267,8 @@ public sealed class ScriptState<T> : ScriptState ...@@ -219,8 +267,8 @@ public sealed class ScriptState<T> : ScriptState
public new T ReturnValue { get; } public new T ReturnValue { get; }
internal override object GetReturnValue() => ReturnValue; internal override object GetReturnValue() => ReturnValue;
internal ScriptState(ScriptExecutionState executionState, T value, Script script) internal ScriptState(ScriptExecutionState executionState, Script script, T value, Exception exceptionOpt)
: base(executionState, script) : base(executionState, script, exceptionOpt)
{ {
ReturnValue = value; ReturnValue = value;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册