提交 110ae706 编写于 作者: T Tomáš Matoušek 提交者: GitHub

Merge pull request #16489 from filipw/feature/script-pdb

Allow emitting debugging symbols via high level scripting APIs
......@@ -58,6 +58,11 @@ private static class _Assembly
.GetDeclaredMethod("Load", typeof(byte[]))
.CreateDelegate<Func<byte[], Assembly>>();
internal static readonly Func<byte[], byte[], Assembly> Load_bytes_with_Pdb = Type
.GetTypeInfo()
.GetDeclaredMethod("Load", typeof(byte[]), typeof(byte[]))
.CreateDelegate<Func<byte[], byte[], Assembly>>();
internal static readonly Func<string, Assembly> LoadFile = Type
.GetTypeInfo()
.GetDeclaredMethod("LoadFile", typeof(string))
......@@ -125,6 +130,16 @@ internal static Assembly LoadAssembly(byte[] peImage)
return _Assembly.Load_bytes(peImage);
}
internal static Assembly LoadAssembly(byte[] peImage, byte[] pdbImage)
{
if (_Assembly.Load_bytes_with_Pdb == null)
{
throw new PlatformNotSupportedException();
}
return _Assembly.Load_bytes_with_Pdb(peImage, pdbImage);
}
internal static Assembly LoadAssembly(string path)
{
if (_Assembly.LoadFile == null)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.CodeAnalysis.Scripting.Hosting;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CSharp.Scripting
{
......@@ -21,9 +24,27 @@ public static class CSharpScript
/// <param name="globalsType">Type of global object.</param>
/// <param name="assemblyLoader">Custom assembly loader.</param>
/// <typeparam name="T">The return type of the script</typeparam>
/// <exception cref="ArgumentNullException">Code is null.</exception>
public static Script<T> Create<T>(string code, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null)
{
return Script.CreateInitialScript<T>(CSharpScriptCompiler.Instance, code, options, globalsType, assemblyLoader);
if (code == null) throw new ArgumentNullException(nameof(code));
return Script.CreateInitialScript<T>(CSharpScriptCompiler.Instance, SourceText.From(code, options?.FileEncoding), options, globalsType, assemblyLoader);
}
/// <summary>
/// Create a new C# script.
/// </summary>
/// <param name="code">The <see cref="Stream"/> representing the source code of the script.</param>
/// <param name="options">The script options.</param>
/// <param name="globalsType">Type of global object.</param>
/// <param name="assemblyLoader">Custom assembly loader.</param>
/// <typeparam name="T">The return type of the script</typeparam>
/// <exception cref="ArgumentNullException">Stream is null.</exception>
/// <exception cref="ArgumentException">Stream is not readable or seekable.</exception>
public static Script<T> Create<T>(Stream code, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null)
{
if (code == null) throw new ArgumentNullException(nameof(code));
return Script.CreateInitialScript<T>(CSharpScriptCompiler.Instance, SourceText.From(code, options?.FileEncoding), options, globalsType, assemblyLoader);
}
/// <summary>
......@@ -33,8 +54,25 @@ public static Script<T> Create<T>(string code, ScriptOptions options = null, Typ
/// <param name="options">The script options.</param>
/// <param name="globalsType">Type of global object.</param>
/// <param name="assemblyLoader">Custom assembly loader.</param>
/// <exception cref="ArgumentNullException">Code is null.</exception>
public static Script<object> Create(string code, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null)
{
if (code == null) throw new ArgumentNullException(nameof(code));
return Create<object>(code, options, globalsType, assemblyLoader);
}
/// <summary>
/// Create a new C# script.
/// </summary>
/// <param name="code">The <see cref="Stream"/> representing the source code of the script.</param>
/// <param name="options">The script options.</param>
/// <param name="globalsType">Type of global object.</param>
/// <param name="assemblyLoader">Custom assembly loader.</param>
/// <exception cref="ArgumentNullException">Stream is null.</exception>
/// <exception cref="ArgumentException">Stream is not readable or seekable.</exception>
public static Script<object> Create(Stream code, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null)
{
if (code == null) throw new ArgumentNullException(nameof(code));
return Create<object>(code, options, globalsType, assemblyLoader);
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Scripting;
......@@ -41,7 +42,7 @@ public override Compilation CreateSubmission(Script script)
// TODO: report diagnostics
diagnostics.Free();
var tree = SyntaxFactory.ParseSyntaxTree(script.Code, s_defaultOptions, script.Options.FilePath);
var tree = SyntaxFactory.ParseSyntaxTree(script.SourceText, s_defaultOptions, script.Options.FilePath);
string assemblyName, submissionTypeName;
script.Builder.GenerateSubmissionId(out assemblyName, out submissionTypeName);
......
static Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.Create(System.IO.Stream code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null, System.Type globalsType = null, Microsoft.CodeAnalysis.Scripting.Hosting.InteractiveAssemblyLoader assemblyLoader = null) -> Microsoft.CodeAnalysis.Scripting.Script<object>
static Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.Create<T>(System.IO.Stream code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null, System.Type globalsType = null, Microsoft.CodeAnalysis.Scripting.Hosting.InteractiveAssemblyLoader assemblyLoader = null) -> Microsoft.CodeAnalysis.Scripting.Script<T>
\ No newline at end of file
......@@ -11,6 +11,8 @@
using Xunit;
using System.IO;
using System.Globalization;
using System.Text;
using System.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests
{
......@@ -29,6 +31,25 @@ public void TestCreateScript()
Assert.Equal("1 + 2", script.Code);
}
[Fact]
public void TestCreateScript_CodeIsNull()
{
Assert.Throws<ArgumentNullException>(() => CSharpScript.Create((string)null));
}
[Fact]
public void TestCreateFromStreamScript()
{
var script = CSharpScript.Create(new MemoryStream(Encoding.UTF8.GetBytes("1 + 2")));
Assert.Equal("1 + 2", script.Code);
}
[Fact]
public void TestCreateFromStreamScript_StreamIsNull()
{
Assert.Throws<ArgumentNullException>(() => CSharpScript.Create((Stream)null));
}
[Fact]
public async Task TestGetCompilation()
{
......@@ -37,6 +58,14 @@ public async Task TestGetCompilation()
Assert.Equal(state.Script.Code, compilation.SyntaxTrees.First().GetText().ToString());
}
[Fact]
public async Task TestGetCompilationSourceText()
{
var state = await CSharpScript.RunAsync("1 + 2", globals: new ScriptTests());
var compilation = state.Script.GetCompilation();
Assert.Equal(state.Script.SourceText, compilation.SyntaxTrees.First().GetText());
}
[Fact]
public void TestCreateScriptDelegate()
{
......@@ -76,6 +105,15 @@ public async Task TestCreateAndRunScript()
Assert.Equal(3, state.ReturnValue);
}
[Fact]
public async Task TestCreateFromStreamAndRunScript()
{
var script = CSharpScript.Create(new MemoryStream(Encoding.UTF8.GetBytes("1 + 2")));
var state = await script.RunAsync();
Assert.Same(script, state.Script);
Assert.Equal(3, state.ReturnValue);
}
[Fact]
public async Task TestEvalScript()
{
......@@ -687,6 +725,99 @@ public async Task LoadedFileWithVoidReturn()
Assert.Equal(0, result);
}
[Fact]
public async Task Pdb_CreateFromString_CodeFromFile_WithEmitDebugInformation_WithoutFileEncoding_CompilationErrorException()
{
var code = "throw new System.Exception();";
try
{
var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFilePath("debug.csx").WithFileEncoding(null);
var script = await CSharpScript.RunAsync(code, opts);
}
catch (CompilationErrorException ex)
{
// CS8055: Cannot emit debug information for a source text without encoding.
ex.Diagnostics.Verify(Diagnostic(ErrorCode.ERR_EncodinglessSyntaxTree, code).WithLocation(1,1));
}
}
[Fact]
public Task Pdb_CreateFromString_CodeFromFile_WithEmitDebugInformation_WithFileEncoding_ResultInPdbEmitted()
{
var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFilePath("debug.csx").WithFileEncoding(Encoding.UTF8);
return VerifyStackTraceAsync(() => CSharpScript.Create("throw new System.Exception();", opts), line: 1, column: 1, filename: "debug.csx");
}
[Fact]
public Task Pdb_CreateFromString_CodeFromFile_WithoutEmitDebugInformation_WithoutFileEncoding_ResultInPdbNotEmitted()
{
var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath(null).WithFileEncoding(null);
return VerifyStackTraceAsync(() => CSharpScript.Create("throw new System.Exception();", opts));
}
[Fact]
public Task Pdb_CreateFromString_CodeFromFile_WithoutEmitDebugInformation_WithFileEncoding_ResultInPdbNotEmitted()
{
var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath("debug.csx").WithFileEncoding(Encoding.UTF8);
return VerifyStackTraceAsync(() => CSharpScript.Create("throw new System.Exception();", opts));
}
[Fact]
public Task Pdb_CreateFromStream_CodeFromFile_WithEmitDebugInformation_ResultInPdbEmitted()
{
var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFilePath("debug.csx");
return VerifyStackTraceAsync(() => CSharpScript.Create(new MemoryStream(Encoding.UTF8.GetBytes("throw new System.Exception();")), opts), line: 1, column: 1, filename: "debug.csx");
}
[Fact]
public Task Pdb_CreateFromStream_CodeFromFile_WithoutEmitDebugInformation_ResultInPdbNotEmitted()
{
var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath("debug.csx");
return VerifyStackTraceAsync(() => CSharpScript.Create(new MemoryStream(Encoding.UTF8.GetBytes("throw new System.Exception();")), opts));
}
[Fact]
public Task Pdb_CreateFromString_InlineCode_WithEmitDebugInformation_WithoutFileEncoding_ResultInPdbEmitted()
{
var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFileEncoding(null);
return VerifyStackTraceAsync(() => CSharpScript.Create("throw new System.Exception();", opts), line: 1, column: 1, filename: "");
}
[Fact]
public Task Pdb_CreateFromString_InlineCode_WithEmitDebugInformation_WithFileEncoding_ResultInPdbEmitted()
{
var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFileEncoding(Encoding.UTF8);
return VerifyStackTraceAsync(() => CSharpScript.Create("throw new System.Exception();", opts), line: 1, column: 1, filename: "");
}
[Fact]
public Task Pdb_CreateFromString_InlineCode_WithoutEmitDebugInformation_WithoutFileEncoding_ResultInPdbNotEmitted()
{
var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFileEncoding(null);
return VerifyStackTraceAsync(() => CSharpScript.Create("throw new System.Exception();", opts));
}
[Fact]
public Task Pdb_CreateFromString_InlineCode_WithoutEmitDebugInformation_WithFileEncoding_ResultInPdbNotEmitted()
{
var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFileEncoding(Encoding.UTF8);
return VerifyStackTraceAsync(() => CSharpScript.Create("throw new System.Exception();", opts));
}
[Fact]
public Task Pdb_CreateFromStream_InlineCode_WithEmitDebugInformation_ResultInPdbEmitted()
{
var opts = ScriptOptions.Default.WithEmitDebugInformation(true);
return VerifyStackTraceAsync(() => CSharpScript.Create(new MemoryStream(Encoding.UTF8.GetBytes("throw new System.Exception();")), opts), line: 1, column: 1, filename: "");
}
[Fact]
public Task Pdb_CreateFromStream_InlineCode_WithoutEmitDebugInformation_ResultInPdbNotEmitted()
{
var opts = ScriptOptions.Default.WithEmitDebugInformation(false);
return VerifyStackTraceAsync(() => CSharpScript.Create(new MemoryStream(Encoding.UTF8.GetBytes("throw new System.Exception();")), opts));
}
[WorkItem(12348, "https://github.com/dotnet/roslyn/issues/12348")]
[Fact]
public async Task StreamWithOffset()
......@@ -739,5 +870,23 @@ public override Stream OpenRead(string resolvedPath)
return stream;
}
}
private async Task VerifyStackTraceAsync(Func<Script<object>> scriptProvider, int line = 0, int column = 0, string filename = null)
{
try
{
var script = scriptProvider();
await script.RunAsync();
}
catch (Exception ex)
{
// line information is only available when PDBs have been emitted
var stackTrace = new StackTrace(ex, needFileInfo: true);
var firstFrame = stackTrace.GetFrames()[0];
Assert.Equal(filename, firstFrame.GetFileName());
Assert.Equal(line, firstFrame.GetFileLineNumber());
Assert.Equal(column, firstFrame.GetFileColumnNumber());
}
}
}
}
......@@ -30,6 +30,15 @@ public override Assembly LoadFromStream(Stream peStream, Stream pdbStream)
{
byte[] peImage = new byte[peStream.Length];
peStream.TryReadAll(peImage, 0, peImage.Length);
if (pdbStream != null)
{
byte[] pdbImage = new byte[pdbStream.Length];
pdbStream.TryReadAll(pdbImage, 0, pdbImage.Length);
return CorLightup.Desktop.LoadAssembly(peImage, pdbImage);
}
return CorLightup.Desktop.LoadAssembly(peImage);
}
......
......@@ -149,7 +149,9 @@ private static ScriptOptions GetScriptOptions(CommandLineArguments arguments, st
references: ImmutableArray.CreateRange(resolvedReferences),
namespaces: CommandLineHelpers.GetImports(arguments),
metadataResolver: metadataResolver,
sourceResolver: sourceResolver);
sourceResolver: sourceResolver,
emitDebugInformation: false,
fileEncoding: null);
}
internal static MetadataReferenceResolver GetMetadataReferenceResolver(CommandLineArguments arguments, TouchedFileLogger loggerOpt)
......@@ -176,7 +178,7 @@ private int RunScript(ScriptOptions options, string code, ErrorLogger errorLogge
var globals = new CommandLineScriptGlobals(_console.Out, _objectFormatter);
globals.Args.AddRange(_compiler.Arguments.ScriptArguments);
var script = Script.CreateInitialScript<int>(_scriptCompiler, code, options, globals.GetType(), assemblyLoaderOpt: null);
var script = Script.CreateInitialScript<int>(_scriptCompiler, SourceText.From(code), options, globals.GetType(), assemblyLoaderOpt: null);
try
{
return script.RunAsync(globals, cancellationToken).Result.ReturnValue;
......@@ -197,7 +199,7 @@ private void RunInteractiveLoop(ScriptOptions options, string initialScriptCodeO
if (initialScriptCodeOpt != null)
{
var script = Script.CreateInitialScript<object>(_scriptCompiler, initialScriptCodeOpt, options, globals.GetType(), assemblyLoaderOpt: null);
var script = Script.CreateInitialScript<object>(_scriptCompiler, SourceText.From(initialScriptCodeOpt), options, globals.GetType(), assemblyLoaderOpt: null);
BuildAndRun(script, globals, ref state, ref options, displayResult: false, cancellationToken: cancellationToken);
}
......@@ -249,7 +251,7 @@ private void RunInteractiveLoop(ScriptOptions options, string initialScriptCodeO
Script<object> newScript;
if (state == null)
{
newScript = Script.CreateInitialScript<object>(_scriptCompiler, code, options, globals.GetType(), assemblyLoaderOpt: null);
newScript = Script.CreateInitialScript<object>(_scriptCompiler, SourceText.From(code ?? string.Empty), options, globals.GetType(), assemblyLoaderOpt: null);
}
else
{
......
Microsoft.CodeAnalysis.Scripting.Script.ContinueWith(System.IO.Stream code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script<object>
Microsoft.CodeAnalysis.Scripting.Script.ContinueWith<TResult>(System.IO.Stream code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script<TResult>
Microsoft.CodeAnalysis.Scripting.ScriptOptions.EmitDebugInformation.get -> bool
Microsoft.CodeAnalysis.Scripting.ScriptOptions.FileEncoding.get -> System.Text.Encoding
Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithEmitDebugInformation(bool emitDebugInformation) -> Microsoft.CodeAnalysis.Scripting.ScriptOptions
Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithFileEncoding(System.Text.Encoding encoding) -> Microsoft.CodeAnalysis.Scripting.ScriptOptions
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
using System;
using System.Collections.Immutable;
......@@ -12,6 +13,8 @@
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.Scripting.Hosting;
using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.Text;
using System.IO;
namespace Microsoft.CodeAnalysis.Scripting
{
......@@ -27,9 +30,9 @@ public abstract class Script
private Compilation _lazyCompilation;
internal Script(ScriptCompiler compiler, ScriptBuilder builder, string code, ScriptOptions options, Type globalsTypeOpt, Script previousOpt)
internal Script(ScriptCompiler compiler, ScriptBuilder builder, SourceText sourceText, ScriptOptions options, Type globalsTypeOpt, Script previousOpt)
{
Debug.Assert(code != null);
Debug.Assert(sourceText != null);
Debug.Assert(options != null);
Debug.Assert(compiler != null);
Debug.Assert(builder != null);
......@@ -37,14 +40,14 @@ internal Script(ScriptCompiler compiler, ScriptBuilder builder, string code, Scr
Compiler = compiler;
Builder = builder;
Previous = previousOpt;
Code = code;
SourceText = sourceText;
Options = options;
GlobalsType = globalsTypeOpt;
}
internal static Script<T> CreateInitialScript<T>(ScriptCompiler compiler, string codeOpt, ScriptOptions optionsOpt, Type globalsTypeOpt, InteractiveAssemblyLoader assemblyLoaderOpt)
internal static Script<T> CreateInitialScript<T>(ScriptCompiler compiler, SourceText sourceText, ScriptOptions optionsOpt, Type globalsTypeOpt, InteractiveAssemblyLoader assemblyLoaderOpt)
{
return new Script<T>(compiler, new ScriptBuilder(assemblyLoaderOpt ?? new InteractiveAssemblyLoader()), codeOpt ?? "", optionsOpt ?? ScriptOptions.Default, globalsTypeOpt, previousOpt: null);
return new Script<T>(compiler, new ScriptBuilder(assemblyLoaderOpt ?? new InteractiveAssemblyLoader()), sourceText, optionsOpt ?? ScriptOptions.Default, globalsTypeOpt, previousOpt: null);
}
/// <summary>
......@@ -62,7 +65,12 @@ internal static Script<T> CreateInitialScript<T>(ScriptCompiler compiler, string
/// <summary>
/// The source code of the script.
/// </summary>
public string Code { get; }
public string Code => SourceText.ToString();
/// <summary>
/// The <see cref="SourceText"/> of the script.
/// </summary>
internal SourceText SourceText { get; }
/// <summary>
/// The type of an object whose members can be accessed by the script as global variables.
......@@ -86,11 +94,34 @@ internal static Script<T> CreateInitialScript<T>(ScriptCompiler compiler, string
public Script<object> ContinueWith(string code, ScriptOptions options = null) =>
ContinueWith<object>(code, options);
/// <summary>
/// Continues the script with given <see cref="Stream"/> representing code.
/// </summary>
/// <exception cref="ArgumentNullException">Stream is null.</exception>
/// <exception cref="ArgumentException">Stream is not readable or seekable.</exception>
public Script<object> ContinueWith(Stream code, ScriptOptions options = null) =>
ContinueWith<object>(code, options);
/// <summary>
/// Continues the script with given code snippet.
/// </summary>
public Script<TResult> ContinueWith<TResult>(string code, ScriptOptions options = null) =>
new Script<TResult>(Compiler, Builder, code ?? "", options ?? InheritOptions(Options), GlobalsType, this);
public Script<TResult> ContinueWith<TResult>(string code, ScriptOptions options = null)
{
options = options ?? InheritOptions(Options);
return new Script<TResult>(Compiler, Builder, SourceText.From(code ?? "", options.FileEncoding), options, GlobalsType, this);
}
/// <summary>
/// Continues the script with given <see cref="Stream"/> representing code.
/// </summary>
/// <exception cref="ArgumentNullException">Stream is null.</exception>
/// <exception cref="ArgumentException">Stream is not readable or seekable.</exception>
public Script<TResult> ContinueWith<TResult>(Stream code, ScriptOptions options = null)
{
if (code == null) throw new ArgumentNullException(nameof(code));
options = options ?? InheritOptions(Options);
return new Script<TResult>(Compiler, Builder, SourceText.From(code, options.FileEncoding), options, GlobalsType, this);
}
private static ScriptOptions InheritOptions(ScriptOptions previous)
{
......@@ -278,8 +309,8 @@ public sealed class Script<T> : Script
private ImmutableArray<Func<object[], Task>> _lazyPrecedingExecutors;
private Func<object[], Task<T>> _lazyExecutor;
internal Script(ScriptCompiler compiler, ScriptBuilder builder, string code, ScriptOptions options, Type globalsTypeOpt, Script previousOpt)
: base(compiler, builder, code, options, globalsTypeOpt, previousOpt)
internal Script(ScriptCompiler compiler, ScriptBuilder builder, SourceText sourceText, ScriptOptions options, Type globalsTypeOpt, Script previousOpt)
: base(compiler, builder, sourceText, options, globalsTypeOpt, previousOpt)
{
}
......@@ -287,7 +318,7 @@ internal Script(ScriptCompiler compiler, ScriptBuilder builder, string code, Scr
public new Script<T> WithOptions(ScriptOptions options)
{
return (options == Options) ? this : new Script<T>(Compiler, Builder, Code, options, GlobalsType, Previous);
return (options == Options) ? this : new Script<T>(Compiler, Builder, SourceText, options, GlobalsType, Previous);
}
internal override Script WithOptionsInternal(ScriptOptions options) => WithOptions(options);
......@@ -325,7 +356,7 @@ internal override ImmutableArray<Diagnostic> CommonCompile(CancellationToken can
{
if (_lazyExecutor == null)
{
Interlocked.CompareExchange(ref _lazyExecutor, Builder.CreateExecutor<T>(Compiler, GetCompilation(), cancellationToken), null);
Interlocked.CompareExchange(ref _lazyExecutor, Builder.CreateExecutor<T>(Compiler, GetCompilation(), Options.EmitDebugInformation, cancellationToken), null);
}
return _lazyExecutor;
......
......@@ -66,7 +66,7 @@ public int GenerateSubmissionId(out string assemblyName, out string typeName)
}
/// <exception cref="CompilationErrorException">Compilation has errors.</exception>
internal Func<object[], Task<T>> CreateExecutor<T>(ScriptCompiler compiler, Compilation compilation, CancellationToken cancellationToken)
internal Func<object[], Task<T>> CreateExecutor<T>(ScriptCompiler compiler, Compilation compilation, bool emitDebugInformation, CancellationToken cancellationToken)
{
var diagnostics = DiagnosticBag.GetInstance();
try
......@@ -76,7 +76,7 @@ public int GenerateSubmissionId(out string assemblyName, out string typeName)
ThrowIfAnyCompilationErrors(diagnostics, compiler.DiagnosticFormatter);
diagnostics.Clear();
var executor = Build<T>(compilation, diagnostics, cancellationToken);
var executor = Build<T>(compilation, diagnostics, emitDebugInformation, cancellationToken);
// emit can fail due to compilation errors or because there is nothing to emit:
ThrowIfAnyCompilationErrors(diagnostics, compiler.DiagnosticFormatter);
......@@ -115,20 +115,29 @@ private static void ThrowIfAnyCompilationErrors(DiagnosticBag diagnostics, Diagn
/// </summary>
private Func<object[], Task<T>> Build<T>(
Compilation compilation,
DiagnosticBag diagnostics,
DiagnosticBag diagnostics,
bool emitDebugInformation,
CancellationToken cancellationToken)
{
var entryPoint = compilation.GetEntryPoint(cancellationToken);
using (var peStream = new MemoryStream())
using (var pdbStreamOpt = emitDebugInformation ? new MemoryStream() : null)
{
var emitOptions = EmitOptions.Default;
if (emitDebugInformation)
{
emitOptions = emitOptions.WithDebugInformationFormat(PdbHelpers.GetPlatformSpecificDebugInformationFormat());
}
var emitResult = compilation.Emit(
peStream: peStream,
pdbStream: null,
pdbStream: pdbStreamOpt,
xmlDocumentationStream: null,
win32Resources: null,
manifestResources: null,
options: EmitOptions.Default,
options: emitOptions,
cancellationToken: cancellationToken);
diagnostics.AddRange(emitResult.Diagnostics);
......@@ -152,7 +161,12 @@ private static void ThrowIfAnyCompilationErrors(DiagnosticBag diagnostics, Diagn
peStream.Position = 0;
var assembly = _assemblyLoader.LoadAssemblyFromStream(peStream, pdbStream: null);
if (pdbStreamOpt != null)
{
pdbStreamOpt.Position = 0;
}
var assembly = _assemblyLoader.LoadAssemblyFromStream(peStream, pdbStreamOpt);
var runtimeEntryPoint = GetEntryPointRuntimeMethod(entryPoint, assembly, cancellationToken);
return runtimeEntryPoint.CreateDelegate<Func<object[], Task<T>>>();
......
......@@ -7,8 +7,9 @@
using System.Linq;
using System.Reflection;
using System.Threading;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Scripting.Hosting;
using Roslyn.Utilities;
using System.Text;
namespace Microsoft.CodeAnalysis.Scripting
{
......@@ -24,7 +25,9 @@ public sealed class ScriptOptions
references: GetDefaultMetadataReferences(),
namespaces: ImmutableArray<string>.Empty,
metadataResolver: RuntimeMetadataReferenceResolver.Default,
sourceResolver: SourceFileResolver.Default);
sourceResolver: SourceFileResolver.Default,
emitDebugInformation: false,
fileEncoding: null);
private static ImmutableArray<MetadataReference> GetDefaultMetadataReferences()
{
......@@ -91,6 +94,17 @@ private static ImmutableArray<MetadataReference> GetDefaultMetadataReferences()
/// </summary>
public ImmutableArray<string> Imports { get; private set; }
/// <summary>
/// Specifies whether debugging symbols should be emitted.
/// </summary>
public bool EmitDebugInformation { get; private set; } = false;
/// <summary>
/// Specifies the encoding to be used when debugging scripts loaded from a file, or saved to a file for debugging purposes.
/// If it's null, the compiler will attempt to detect the necessary encoding for debugging
/// </summary>
public Encoding FileEncoding { get; private set; }
/// <summary>
/// The path to the script source if it originated from a file, empty otherwise.
/// </summary>
......@@ -101,7 +115,9 @@ private static ImmutableArray<MetadataReference> GetDefaultMetadataReferences()
ImmutableArray<MetadataReference> references,
ImmutableArray<string> namespaces,
MetadataReferenceResolver metadataResolver,
SourceReferenceResolver sourceResolver)
SourceReferenceResolver sourceResolver,
bool emitDebugInformation,
Encoding fileEncoding)
{
Debug.Assert(filePath != null);
Debug.Assert(!references.IsDefault);
......@@ -114,6 +130,8 @@ private static ImmutableArray<MetadataReference> GetDefaultMetadataReferences()
Imports = namespaces;
MetadataResolver = metadataResolver;
SourceResolver = sourceResolver;
EmitDebugInformation = emitDebugInformation;
FileEncoding = fileEncoding;
}
private ScriptOptions(ScriptOptions other)
......@@ -121,7 +139,9 @@ private ScriptOptions(ScriptOptions other)
references: other.MetadataReferences,
namespaces: other.Imports,
metadataResolver: other.MetadataResolver,
sourceResolver: other.SourceResolver)
sourceResolver: other.SourceResolver,
emitDebugInformation: other.EmitDebugInformation,
fileEncoding: other.FileEncoding)
{
}
......@@ -282,5 +302,17 @@ private static MetadataReference CreateReferenceFromAssembly(Assembly assembly)
/// <exception cref="ArgumentNullException"><paramref name="imports"/> is null or contains a null reference.</exception>
public ScriptOptions AddImports(params string[] imports) =>
AddImports((IEnumerable<string>)imports);
/// <summary>
/// Creates a new <see cref="ScriptOptions"/> with debugging information enabled.
/// </summary>
public ScriptOptions WithEmitDebugInformation(bool emitDebugInformation) =>
emitDebugInformation == EmitDebugInformation ? this : new ScriptOptions(this) { EmitDebugInformation = emitDebugInformation };
/// <summary>
/// Creates a new <see cref="ScriptOptions"/> with specified <see cref="FileEncoding"/>.
/// </summary>
public ScriptOptions WithFileEncoding(Encoding encoding) =>
encoding == FileEncoding ? this : new ScriptOptions(this) { FileEncoding = encoding };
}
}
......@@ -108,6 +108,7 @@
<Compile Include="Utilities\ConsoleShims.cs" />
<Compile Include="Utilities\IListExtensions.cs" />
<Compile Include="Utilities\ParameterValidationHelpers.cs" />
<Compile Include="Utilities\PdbHelpers.cs" />
<Compile Include="Utilities\TaskExtensions.cs" />
</ItemGroup>
<ItemGroup>
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.CodeAnalysis.Emit;
namespace Microsoft.CodeAnalysis.Scripting
{
internal static class PdbHelpers
{
public static DebugInformationFormat GetPlatformSpecificDebugInformationFormat()
{
// for CoreCLR & Mono, use PortablePdb
if (CoreClrShim.AssemblyLoadContext.Type != null || Type.GetType("Mono.Runtime") != null)
{
return DebugInformationFormat.PortablePdb;
}
// otherwise standard PDB
return DebugInformationFormat.Pdb;
}
}
}
......@@ -4,6 +4,8 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Reflection;
using System.Text;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Scripting.Hosting;
using Roslyn.Test.Utilities;
using Xunit;
......@@ -148,5 +150,34 @@ public void WithImports_Errors()
options.WithImports("b\0lah");
options.WithImports(".blah");
}
[Fact]
public void WithEmitDebugInformation_SetsEmitDebugInformation()
{
Assert.True(ScriptOptions.Default.WithEmitDebugInformation(true).EmitDebugInformation);
Assert.False(ScriptOptions.Default.WithEmitDebugInformation(false).EmitDebugInformation);
Assert.False(ScriptOptions.Default.EmitDebugInformation);
}
[Fact]
public void WithEmitDebugInformation_SameValueTwice_DoesNotCreateNewInstance()
{
var options = ScriptOptions.Default.WithEmitDebugInformation(true);
Assert.Same(options, options.WithEmitDebugInformation(true));
}
[Fact]
public void WithFileEncoding_SetsWithFileEncoding()
{
var options = ScriptOptions.Default.WithFileEncoding(Encoding.ASCII);
Assert.Equal(Encoding.ASCII, options.FileEncoding);
}
[Fact]
public void WithFileEncoding_SameValueTwice_DoesNotCreateNewInstance()
{
var options = ScriptOptions.Default.WithFileEncoding(Encoding.ASCII);
Assert.Same(options, options.WithFileEncoding(Encoding.ASCII));
}
}
}
......@@ -4,6 +4,7 @@ Imports System.Threading
Imports System.Threading.Tasks
Imports Microsoft.CodeAnalysis.Scripting
Imports Microsoft.CodeAnalysis.Scripting.Hosting
Imports Microsoft.CodeAnalysis.Text
Namespace Microsoft.CodeAnalysis.VisualBasic.Scripting
......@@ -21,7 +22,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Scripting
Optional options As ScriptOptions = Nothing,
Optional globalsType As Type = Nothing,
Optional assemblyLoader As InteractiveAssemblyLoader = Nothing) As Script(Of T)
Return Script.CreateInitialScript(Of T)(VisualBasicScriptCompiler.Instance, code, options, globalsType, assemblyLoader)
Return Script.CreateInitialScript(Of T)(VisualBasicScriptCompiler.Instance, SourceText.From(If(code, String.Empty)), options, globalsType, assemblyLoader)
End Function
''' <summary>
......
......@@ -76,7 +76,9 @@
<ItemGroup>
<None Include="project.json" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<Compile Include="CodeGeneration\SymbolEditorTests.cs" />
<Compile Include="CodeGeneration\SyntaxGeneratorTests.cs" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册