From 7973ec115664cca0e07f972c89ea6c82b6e1c82d Mon Sep 17 00:00:00 2001 From: filipw Date: Fri, 13 Jan 2017 08:46:56 +0100 Subject: [PATCH 01/20] added debugging for scripting --- src/Compilers/Core/Portable/CorLightup.cs | 15 +++++++++++++++ src/Scripting/CSharp/CSharpScriptCompiler.cs | 11 +++++++++-- .../AssemblyLoader/DesktopAssemblyLoaderImpl.cs | 8 ++++++++ src/Scripting/Core/PublicAPI.Unshipped.txt | 2 ++ src/Scripting/Core/ScriptBuilder.cs | 6 ++++-- src/Scripting/Core/ScriptCompiler.cs | 2 ++ src/Scripting/Core/ScriptOptions.cs | 11 +++++++++++ 7 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/Compilers/Core/Portable/CorLightup.cs b/src/Compilers/Core/Portable/CorLightup.cs index 0d217b4c16885..4785b956f4c52 100644 --- a/src/Compilers/Core/Portable/CorLightup.cs +++ b/src/Compilers/Core/Portable/CorLightup.cs @@ -58,6 +58,11 @@ private static class _Assembly .GetDeclaredMethod("Load", typeof(byte[])) .CreateDelegate>(); + internal static readonly Func Load_bytes_with_Pdb = Type + .GetTypeInfo() + .GetDeclaredMethod("Load", typeof(byte[]), typeof(byte[])) + .CreateDelegate>(); + internal static readonly Func 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 == null) + { + throw new PlatformNotSupportedException(); + } + + return _Assembly.Load_bytes_with_Pdb(peImage, pdbImage); + } + internal static Assembly LoadAssembly(string path) { if (_Assembly.LoadFile == null) diff --git a/src/Scripting/CSharp/CSharpScriptCompiler.cs b/src/Scripting/CSharp/CSharpScriptCompiler.cs index b40a95bf0b174..0dfc56b863b55 100644 --- a/src/Scripting/CSharp/CSharpScriptCompiler.cs +++ b/src/Scripting/CSharp/CSharpScriptCompiler.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Text; using System.Threading; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Scripting; @@ -10,6 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Scripting { internal sealed class CSharpScriptCompiler : ScriptCompiler { + private bool _emitPdb = false; + public static readonly ScriptCompiler Instance = new CSharpScriptCompiler(); private static readonly CSharpParseOptions s_defaultOptions = new CSharpParseOptions(kind: SourceCodeKind.Script); @@ -22,6 +25,8 @@ private CSharpScriptCompiler() public override StringComparer IdentifierComparer => StringComparer.Ordinal; + public override bool EmitPdb => _emitPdb; + public override bool IsCompleteSubmission(SyntaxTree tree) => SyntaxFactory.IsCompleteSubmission(tree); public override SyntaxTree ParseSubmission(SourceText text, CancellationToken cancellationToken) => @@ -35,13 +40,15 @@ public override Compilation CreateSubmission(Script script) previousSubmission = (CSharpCompilation)script.Previous.GetCompilation(); } + _emitPdb = script.Options.EmitPdb; + var diagnostics = DiagnosticBag.GetInstance(); var references = script.GetReferencesForCompilation(MessageProvider.Instance, diagnostics); // TODO: report diagnostics diagnostics.Free(); - var tree = SyntaxFactory.ParseSyntaxTree(script.Code, s_defaultOptions, script.Options.FilePath); + var tree = SyntaxFactory.ParseSyntaxTree(script.Code, s_defaultOptions, script.Options.FilePath, encoding: Encoding.UTF8); string assemblyName, submissionTypeName; script.Builder.GenerateSubmissionId(out assemblyName, out submissionTypeName); @@ -55,7 +62,7 @@ public override Compilation CreateSubmission(Script script) mainTypeName: null, scriptClassName: submissionTypeName, usings: script.Options.Imports, - optimizationLevel: OptimizationLevel.Debug, // TODO + optimizationLevel: EmitPdb ? OptimizationLevel.Debug : OptimizationLevel.Release, checkOverflow: false, // TODO allowUnsafe: true, // TODO platform: Platform.AnyCpu, diff --git a/src/Scripting/Core/Hosting/AssemblyLoader/DesktopAssemblyLoaderImpl.cs b/src/Scripting/Core/Hosting/AssemblyLoader/DesktopAssemblyLoaderImpl.cs index 72981532ca593..fb22f4f8b4b43 100644 --- a/src/Scripting/Core/Hosting/AssemblyLoader/DesktopAssemblyLoaderImpl.cs +++ b/src/Scripting/Core/Hosting/AssemblyLoader/DesktopAssemblyLoaderImpl.cs @@ -30,6 +30,14 @@ 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); } diff --git a/src/Scripting/Core/PublicAPI.Unshipped.txt b/src/Scripting/Core/PublicAPI.Unshipped.txt index e69de29bb2d1d..e65c646f56504 100644 --- a/src/Scripting/Core/PublicAPI.Unshipped.txt +++ b/src/Scripting/Core/PublicAPI.Unshipped.txt @@ -0,0 +1,2 @@ +Microsoft.CodeAnalysis.Scripting.ScriptOptions.EmitPdb.get -> bool +Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithPdb() -> Microsoft.CodeAnalysis.Scripting.ScriptOptions \ No newline at end of file diff --git a/src/Scripting/Core/ScriptBuilder.cs b/src/Scripting/Core/ScriptBuilder.cs index f3d0ffdbaa1a2..ba5e2c4e57875 100644 --- a/src/Scripting/Core/ScriptBuilder.cs +++ b/src/Scripting/Core/ScriptBuilder.cs @@ -121,10 +121,11 @@ private Func> Build( var entryPoint = compilation.GetEntryPoint(cancellationToken); using (var peStream = new MemoryStream()) + using (var pdbStream = new MemoryStream()) { var emitResult = compilation.Emit( peStream: peStream, - pdbStream: null, + pdbStream: pdbStream, xmlDocumentationStream: null, win32Resources: null, manifestResources: null, @@ -151,8 +152,9 @@ private Func> Build( } peStream.Position = 0; + pdbStream.Position = 0; - var assembly = _assemblyLoader.LoadAssemblyFromStream(peStream, pdbStream: null); + var assembly = _assemblyLoader.LoadAssemblyFromStream(peStream, pdbStream); var runtimeEntryPoint = GetEntryPointRuntimeMethod(entryPoint, assembly, cancellationToken); return runtimeEntryPoint.CreateDelegate>>(); diff --git a/src/Scripting/Core/ScriptCompiler.cs b/src/Scripting/Core/ScriptCompiler.cs index 2f0963825ab20..e4c949f71c37f 100644 --- a/src/Scripting/Core/ScriptCompiler.cs +++ b/src/Scripting/Core/ScriptCompiler.cs @@ -15,5 +15,7 @@ internal abstract class ScriptCompiler public abstract SyntaxTree ParseSubmission(SourceText text, CancellationToken cancellationToken); public abstract bool IsCompleteSubmission(SyntaxTree tree); + + public abstract bool EmitPdb { get; } } } diff --git a/src/Scripting/Core/ScriptOptions.cs b/src/Scripting/Core/ScriptOptions.cs index 42eb09ef37eb5..3d2f990226f76 100644 --- a/src/Scripting/Core/ScriptOptions.cs +++ b/src/Scripting/Core/ScriptOptions.cs @@ -91,6 +91,11 @@ private static ImmutableArray GetDefaultMetadataReferences() /// public ImmutableArray Imports { get; private set; } + /// + /// Specifies whether PDBs for debugging should be emitted. + /// + public bool EmitPdb { get; private set; } = false; + /// /// The path to the script source if it originated from a file, empty otherwise. /// @@ -282,5 +287,11 @@ public ScriptOptions AddImports(IEnumerable imports) => /// is null or contains a null reference. public ScriptOptions AddImports(params string[] imports) => AddImports((IEnumerable)imports); + + /// + /// Creates a new with specified . + /// + public ScriptOptions WithPdb() => + new ScriptOptions(this) { EmitPdb = !EmitPdb }; } } From d722d4d320c66cbcd2f51c4316c303bc5ddb4f81 Mon Sep 17 00:00:00 2001 From: filipw Date: Fri, 13 Jan 2017 09:25:48 +0100 Subject: [PATCH 02/20] added encoding to ScriptOptions + some clean up --- src/Compilers/Core/Portable/CorLightup.cs | 2 +- src/Scripting/CSharp/CSharpScriptCompiler.cs | 2 +- src/Scripting/Core/PublicAPI.Unshipped.txt | 4 +++- src/Scripting/Core/ScriptBuilder.cs | 12 +++++++---- src/Scripting/Core/ScriptCompiler.cs | 2 +- src/Scripting/Core/ScriptOptions.cs | 21 ++++++++++++++++---- src/Scripting/CoreTest/ScriptOptionsTests.cs | 7 +++++++ 7 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/Compilers/Core/Portable/CorLightup.cs b/src/Compilers/Core/Portable/CorLightup.cs index 4785b956f4c52..9138ea0e43d68 100644 --- a/src/Compilers/Core/Portable/CorLightup.cs +++ b/src/Compilers/Core/Portable/CorLightup.cs @@ -132,7 +132,7 @@ internal static Assembly LoadAssembly(byte[] peImage) internal static Assembly LoadAssembly(byte[] peImage, byte[] pdbImage) { - if (_Assembly.Load_bytes == null) + if (_Assembly.Load_bytes_with_Pdb == null) { throw new PlatformNotSupportedException(); } diff --git a/src/Scripting/CSharp/CSharpScriptCompiler.cs b/src/Scripting/CSharp/CSharpScriptCompiler.cs index 0dfc56b863b55..2e5fb4f2be5f8 100644 --- a/src/Scripting/CSharp/CSharpScriptCompiler.cs +++ b/src/Scripting/CSharp/CSharpScriptCompiler.cs @@ -48,7 +48,7 @@ public override Compilation CreateSubmission(Script script) // TODO: report diagnostics diagnostics.Free(); - var tree = SyntaxFactory.ParseSyntaxTree(script.Code, s_defaultOptions, script.Options.FilePath, encoding: Encoding.UTF8); + var tree = SyntaxFactory.ParseSyntaxTree(script.Code, s_defaultOptions, script.Options.FilePath, encoding: script.Options.Encoding); string assemblyName, submissionTypeName; script.Builder.GenerateSubmissionId(out assemblyName, out submissionTypeName); diff --git a/src/Scripting/Core/PublicAPI.Unshipped.txt b/src/Scripting/Core/PublicAPI.Unshipped.txt index e65c646f56504..e4d90ff5517ee 100644 --- a/src/Scripting/Core/PublicAPI.Unshipped.txt +++ b/src/Scripting/Core/PublicAPI.Unshipped.txt @@ -1,2 +1,4 @@ Microsoft.CodeAnalysis.Scripting.ScriptOptions.EmitPdb.get -> bool -Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithPdb() -> Microsoft.CodeAnalysis.Scripting.ScriptOptions \ No newline at end of file +Microsoft.CodeAnalysis.Scripting.ScriptOptions.Encoding.get -> System.Text.Encoding +Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithEncoding(System.Text.Encoding encoding) -> Microsoft.CodeAnalysis.Scripting.ScriptOptions +Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithPdb(bool emitPdb) -> Microsoft.CodeAnalysis.Scripting.ScriptOptions \ No newline at end of file diff --git a/src/Scripting/Core/ScriptBuilder.cs b/src/Scripting/Core/ScriptBuilder.cs index ba5e2c4e57875..a628738ad056f 100644 --- a/src/Scripting/Core/ScriptBuilder.cs +++ b/src/Scripting/Core/ScriptBuilder.cs @@ -76,7 +76,7 @@ internal Func> CreateExecutor(ScriptCompiler compiler, Comp ThrowIfAnyCompilationErrors(diagnostics, compiler.DiagnosticFormatter); diagnostics.Clear(); - var executor = Build(compilation, diagnostics, cancellationToken); + var executor = Build(compilation, diagnostics, compiler.EmitPdb, cancellationToken); // emit can fail due to compilation errors or because there is nothing to emit: ThrowIfAnyCompilationErrors(diagnostics, compiler.DiagnosticFormatter); @@ -115,13 +115,13 @@ private static void ThrowIfAnyCompilationErrors(DiagnosticBag diagnostics, Diagn /// private Func> Build( Compilation compilation, - DiagnosticBag diagnostics, + DiagnosticBag diagnostics, bool emitPdb, CancellationToken cancellationToken) { var entryPoint = compilation.GetEntryPoint(cancellationToken); using (var peStream = new MemoryStream()) - using (var pdbStream = new MemoryStream()) + using (var pdbStream = emitPdb ? new MemoryStream() : null) { var emitResult = compilation.Emit( peStream: peStream, @@ -152,7 +152,11 @@ private Func> Build( } peStream.Position = 0; - pdbStream.Position = 0; + + if (pdbStream != null) + { + pdbStream.Position = 0; + } var assembly = _assemblyLoader.LoadAssemblyFromStream(peStream, pdbStream); var runtimeEntryPoint = GetEntryPointRuntimeMethod(entryPoint, assembly, cancellationToken); diff --git a/src/Scripting/Core/ScriptCompiler.cs b/src/Scripting/Core/ScriptCompiler.cs index e4c949f71c37f..1060a09212b65 100644 --- a/src/Scripting/Core/ScriptCompiler.cs +++ b/src/Scripting/Core/ScriptCompiler.cs @@ -16,6 +16,6 @@ internal abstract class ScriptCompiler public abstract SyntaxTree ParseSubmission(SourceText text, CancellationToken cancellationToken); public abstract bool IsCompleteSubmission(SyntaxTree tree); - public abstract bool EmitPdb { get; } + public virtual bool EmitPdb { get; } = false; } } diff --git a/src/Scripting/Core/ScriptOptions.cs b/src/Scripting/Core/ScriptOptions.cs index 3d2f990226f76..4cff28a7dbfc7 100644 --- a/src/Scripting/Core/ScriptOptions.cs +++ b/src/Scripting/Core/ScriptOptions.cs @@ -8,7 +8,7 @@ using System.Reflection; using System.Threading; using Microsoft.CodeAnalysis.Scripting.Hosting; -using Roslyn.Utilities; +using System.Text; namespace Microsoft.CodeAnalysis.Scripting { @@ -96,6 +96,11 @@ private static ImmutableArray GetDefaultMetadataReferences() /// public bool EmitPdb { get; private set; } = false; + /// + /// Specifies the encoding to be used for the code. + /// + public Encoding Encoding { get; private set; } = Encoding.UTF8; + /// /// The path to the script source if it originated from a file, empty otherwise. /// @@ -128,6 +133,8 @@ private ScriptOptions(ScriptOptions other) metadataResolver: other.MetadataResolver, sourceResolver: other.SourceResolver) { + EmitPdb = other.EmitPdb; + Encoding = other.Encoding; } // a reference to an assembly should by default be equivalent to #r, which applies recursive global alias: @@ -289,9 +296,15 @@ public ScriptOptions AddImports(params string[] imports) => AddImports((IEnumerable)imports); /// - /// Creates a new with specified . + /// Creates a new with specified . + /// + public ScriptOptions WithPdb(bool emitPdb) => + new ScriptOptions(this) { EmitPdb = emitPdb }; + + /// + /// Creates a new with specified . /// - public ScriptOptions WithPdb() => - new ScriptOptions(this) { EmitPdb = !EmitPdb }; + public ScriptOptions WithEncoding(Encoding encoding) => + new ScriptOptions(this) { Encoding = encoding }; } } diff --git a/src/Scripting/CoreTest/ScriptOptionsTests.cs b/src/Scripting/CoreTest/ScriptOptionsTests.cs index 9fa2d968ba358..40c3b490fa9e1 100644 --- a/src/Scripting/CoreTest/ScriptOptionsTests.cs +++ b/src/Scripting/CoreTest/ScriptOptionsTests.cs @@ -148,5 +148,12 @@ public void WithImports_Errors() options.WithImports("b\0lah"); options.WithImports(".blah"); } + + [Fact] + public void WithPdb_SetsEmitPdb() + { + var options = ScriptOptions.Default.WithPdb(true); + Assert.Equal(true, options.EmitPdb); + } } } From b480b9c7933c3657480f35629572757cfee6b318 Mon Sep 17 00:00:00 2001 From: filipw Date: Fri, 13 Jan 2017 10:03:54 +0100 Subject: [PATCH 03/20] sightly improved approach, with DebugInformationFormat --- src/Scripting/CSharp/CSharpScriptCompiler.cs | 8 +------- src/Scripting/Core/PublicAPI.Unshipped.txt | 7 ++++--- src/Scripting/Core/Script.cs | 2 +- src/Scripting/Core/ScriptBuilder.cs | 17 ++++++++++++----- src/Scripting/Core/ScriptCompiler.cs | 3 +-- src/Scripting/Core/ScriptOptions.cs | 18 ++++++++++++------ 6 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/Scripting/CSharp/CSharpScriptCompiler.cs b/src/Scripting/CSharp/CSharpScriptCompiler.cs index 2e5fb4f2be5f8..2436fbb225e0a 100644 --- a/src/Scripting/CSharp/CSharpScriptCompiler.cs +++ b/src/Scripting/CSharp/CSharpScriptCompiler.cs @@ -11,8 +11,6 @@ namespace Microsoft.CodeAnalysis.CSharp.Scripting { internal sealed class CSharpScriptCompiler : ScriptCompiler { - private bool _emitPdb = false; - public static readonly ScriptCompiler Instance = new CSharpScriptCompiler(); private static readonly CSharpParseOptions s_defaultOptions = new CSharpParseOptions(kind: SourceCodeKind.Script); @@ -25,8 +23,6 @@ private CSharpScriptCompiler() public override StringComparer IdentifierComparer => StringComparer.Ordinal; - public override bool EmitPdb => _emitPdb; - public override bool IsCompleteSubmission(SyntaxTree tree) => SyntaxFactory.IsCompleteSubmission(tree); public override SyntaxTree ParseSubmission(SourceText text, CancellationToken cancellationToken) => @@ -40,8 +36,6 @@ public override Compilation CreateSubmission(Script script) previousSubmission = (CSharpCompilation)script.Previous.GetCompilation(); } - _emitPdb = script.Options.EmitPdb; - var diagnostics = DiagnosticBag.GetInstance(); var references = script.GetReferencesForCompilation(MessageProvider.Instance, diagnostics); @@ -62,7 +56,7 @@ public override Compilation CreateSubmission(Script script) mainTypeName: null, scriptClassName: submissionTypeName, usings: script.Options.Imports, - optimizationLevel: EmitPdb ? OptimizationLevel.Debug : OptimizationLevel.Release, + optimizationLevel: script.Options.EmitDebugInformation ? OptimizationLevel.Debug : OptimizationLevel.Release, checkOverflow: false, // TODO allowUnsafe: true, // TODO platform: Platform.AnyCpu, diff --git a/src/Scripting/Core/PublicAPI.Unshipped.txt b/src/Scripting/Core/PublicAPI.Unshipped.txt index e4d90ff5517ee..a42ed1070fcb2 100644 --- a/src/Scripting/Core/PublicAPI.Unshipped.txt +++ b/src/Scripting/Core/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ -Microsoft.CodeAnalysis.Scripting.ScriptOptions.EmitPdb.get -> bool +Microsoft.CodeAnalysis.Scripting.ScriptOptions.DebugInformationFormat.get -> Microsoft.CodeAnalysis.Emit.DebugInformationFormat? +Microsoft.CodeAnalysis.Scripting.ScriptOptions.EmitDebugInformation.get -> bool Microsoft.CodeAnalysis.Scripting.ScriptOptions.Encoding.get -> System.Text.Encoding -Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithEncoding(System.Text.Encoding encoding) -> Microsoft.CodeAnalysis.Scripting.ScriptOptions -Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithPdb(bool emitPdb) -> Microsoft.CodeAnalysis.Scripting.ScriptOptions \ No newline at end of file +Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithDebugInformation(Microsoft.CodeAnalysis.Emit.DebugInformationFormat pdbFormat) -> Microsoft.CodeAnalysis.Scripting.ScriptOptions +Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithEncoding(System.Text.Encoding encoding) -> Microsoft.CodeAnalysis.Scripting.ScriptOptions \ No newline at end of file diff --git a/src/Scripting/Core/Script.cs b/src/Scripting/Core/Script.cs index 1d21552cb5010..1055b8f9b42ce 100644 --- a/src/Scripting/Core/Script.cs +++ b/src/Scripting/Core/Script.cs @@ -325,7 +325,7 @@ private Func> GetExecutor(CancellationToken cancellationToken) { if (_lazyExecutor == null) { - Interlocked.CompareExchange(ref _lazyExecutor, Builder.CreateExecutor(Compiler, GetCompilation(), cancellationToken), null); + Interlocked.CompareExchange(ref _lazyExecutor, Builder.CreateExecutor(Compiler, GetCompilation(), Options, cancellationToken), null); } return _lazyExecutor; diff --git a/src/Scripting/Core/ScriptBuilder.cs b/src/Scripting/Core/ScriptBuilder.cs index a628738ad056f..45d778e6cbc76 100644 --- a/src/Scripting/Core/ScriptBuilder.cs +++ b/src/Scripting/Core/ScriptBuilder.cs @@ -66,7 +66,7 @@ public int GenerateSubmissionId(out string assemblyName, out string typeName) } /// Compilation has errors. - internal Func> CreateExecutor(ScriptCompiler compiler, Compilation compilation, CancellationToken cancellationToken) + internal Func> CreateExecutor(ScriptCompiler compiler, Compilation compilation, ScriptOptions scriptOptions, CancellationToken cancellationToken) { var diagnostics = DiagnosticBag.GetInstance(); try @@ -76,7 +76,7 @@ internal Func> CreateExecutor(ScriptCompiler compiler, Comp ThrowIfAnyCompilationErrors(diagnostics, compiler.DiagnosticFormatter); diagnostics.Clear(); - var executor = Build(compilation, diagnostics, compiler.EmitPdb, cancellationToken); + var executor = Build(compilation, diagnostics, scriptOptions, cancellationToken); // emit can fail due to compilation errors or because there is nothing to emit: ThrowIfAnyCompilationErrors(diagnostics, compiler.DiagnosticFormatter); @@ -115,21 +115,28 @@ private static void ThrowIfAnyCompilationErrors(DiagnosticBag diagnostics, Diagn /// private Func> Build( Compilation compilation, - DiagnosticBag diagnostics, bool emitPdb, + DiagnosticBag diagnostics, ScriptOptions scriptOptions, CancellationToken cancellationToken) { var entryPoint = compilation.GetEntryPoint(cancellationToken); using (var peStream = new MemoryStream()) - using (var pdbStream = emitPdb ? new MemoryStream() : null) + using (var pdbStream = scriptOptions.EmitDebugInformation ? new MemoryStream() : null) { + var emitOptions = EmitOptions.Default; + + if (scriptOptions.EmitDebugInformation) + { + emitOptions = emitOptions.WithDebugInformationFormat(scriptOptions.DebugInformationFormat.Value); + } + var emitResult = compilation.Emit( peStream: peStream, pdbStream: pdbStream, xmlDocumentationStream: null, win32Resources: null, manifestResources: null, - options: EmitOptions.Default, + options: emitOptions, cancellationToken: cancellationToken); diagnostics.AddRange(emitResult.Diagnostics); diff --git a/src/Scripting/Core/ScriptCompiler.cs b/src/Scripting/Core/ScriptCompiler.cs index 1060a09212b65..f86a208439bd9 100644 --- a/src/Scripting/Core/ScriptCompiler.cs +++ b/src/Scripting/Core/ScriptCompiler.cs @@ -3,6 +3,7 @@ using System; using System.Text; using System.Threading; +using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Scripting @@ -15,7 +16,5 @@ internal abstract class ScriptCompiler public abstract SyntaxTree ParseSubmission(SourceText text, CancellationToken cancellationToken); public abstract bool IsCompleteSubmission(SyntaxTree tree); - - public virtual bool EmitPdb { get; } = false; } } diff --git a/src/Scripting/Core/ScriptOptions.cs b/src/Scripting/Core/ScriptOptions.cs index 4cff28a7dbfc7..2107507fb897a 100644 --- a/src/Scripting/Core/ScriptOptions.cs +++ b/src/Scripting/Core/ScriptOptions.cs @@ -12,6 +12,7 @@ namespace Microsoft.CodeAnalysis.Scripting { + using Microsoft.CodeAnalysis.Emit; using static ParameterValidationHelpers; /// @@ -92,9 +93,14 @@ private static ImmutableArray GetDefaultMetadataReferences() public ImmutableArray Imports { get; private set; } /// - /// Specifies whether PDBs for debugging should be emitted. + /// Specifies whether debugging symbols should be emitted. /// - public bool EmitPdb { get; private set; } = false; + public bool EmitDebugInformation => DebugInformationFormat != null; + + /// + /// to be used for debugging symbols if needed + /// + public DebugInformationFormat? DebugInformationFormat { get; private set; } = null; /// /// Specifies the encoding to be used for the code. @@ -133,7 +139,7 @@ private ScriptOptions(ScriptOptions other) metadataResolver: other.MetadataResolver, sourceResolver: other.SourceResolver) { - EmitPdb = other.EmitPdb; + DebugInformationFormat = other.DebugInformationFormat; Encoding = other.Encoding; } @@ -296,10 +302,10 @@ public ScriptOptions AddImports(params string[] imports) => AddImports((IEnumerable)imports); /// - /// Creates a new with specified . + /// Creates a new with specified . /// - public ScriptOptions WithPdb(bool emitPdb) => - new ScriptOptions(this) { EmitPdb = emitPdb }; + public ScriptOptions WithDebugInformation(DebugInformationFormat pdbFormat) => + new ScriptOptions(this) { DebugInformationFormat = pdbFormat }; /// /// Creates a new with specified . From c1ded03524810bd219ba9d564f7997edff5d97c3 Mon Sep 17 00:00:00 2001 From: filipw Date: Fri, 13 Jan 2017 10:42:32 +0100 Subject: [PATCH 04/20] fixed test --- src/Scripting/CoreTest/ScriptOptionsTests.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Scripting/CoreTest/ScriptOptionsTests.cs b/src/Scripting/CoreTest/ScriptOptionsTests.cs index 40c3b490fa9e1..96770c4d0e8cf 100644 --- a/src/Scripting/CoreTest/ScriptOptionsTests.cs +++ b/src/Scripting/CoreTest/ScriptOptionsTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Reflection; +using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Scripting.Hosting; using Roslyn.Test.Utilities; using Xunit; @@ -150,10 +151,12 @@ public void WithImports_Errors() } [Fact] - public void WithPdb_SetsEmitPdb() + public void WithDebugInformation_SetsEmitDebugInformation_SetsDebugInformation() { - var options = ScriptOptions.Default.WithPdb(true); - Assert.Equal(true, options.EmitPdb); + var format = DebugInformationFormat.Pdb; + var options = ScriptOptions.Default.WithDebugInformation(format); + Assert.Equal(true, options.EmitDebugInformation); + Assert.Equal(format, options.DebugInformationFormat); } } } From 12eca6f5f76dd3335f0a8867fa5fa700de84cbb0 Mon Sep 17 00:00:00 2001 From: filipw Date: Fri, 13 Jan 2017 10:59:22 +0100 Subject: [PATCH 05/20] revert setting of optimizations --- src/Scripting/CSharp/CSharpScriptCompiler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Scripting/CSharp/CSharpScriptCompiler.cs b/src/Scripting/CSharp/CSharpScriptCompiler.cs index 2436fbb225e0a..a9e99eedca9bb 100644 --- a/src/Scripting/CSharp/CSharpScriptCompiler.cs +++ b/src/Scripting/CSharp/CSharpScriptCompiler.cs @@ -56,7 +56,7 @@ public override Compilation CreateSubmission(Script script) mainTypeName: null, scriptClassName: submissionTypeName, usings: script.Options.Imports, - optimizationLevel: script.Options.EmitDebugInformation ? OptimizationLevel.Debug : OptimizationLevel.Release, + optimizationLevel: OptimizationLevel.Debug, // TODO checkOverflow: false, // TODO allowUnsafe: true, // TODO platform: Platform.AnyCpu, From e2118362c6a429c8d081ac0cc97c0265613bc20c Mon Sep 17 00:00:00 2001 From: filipw Date: Tue, 17 Jan 2017 13:14:37 +0100 Subject: [PATCH 06/20] addressed code review feedback --- src/Scripting/CSharp/CSharpScriptCompiler.cs | 2 +- .../DesktopAssemblyLoaderImpl.cs | 3 +- src/Scripting/Core/PublicAPI.Unshipped.txt | 7 ++--- src/Scripting/Core/ScriptBuilder.cs | 19 ++++++------ src/Scripting/Core/ScriptCompiler.cs | 1 - src/Scripting/Core/ScriptOptions.cs | 29 ++++++++----------- src/Scripting/Core/Scripting.csproj | 1 + src/Scripting/Core/Utilities/PdbHelpers.cs | 22 ++++++++++++++ src/Scripting/CoreTest/ScriptOptionsTests.cs | 6 ++-- .../CSharpTest/CSharpServicesTest.csproj | 4 ++- 10 files changed, 56 insertions(+), 38 deletions(-) create mode 100644 src/Scripting/Core/Utilities/PdbHelpers.cs diff --git a/src/Scripting/CSharp/CSharpScriptCompiler.cs b/src/Scripting/CSharp/CSharpScriptCompiler.cs index a9e99eedca9bb..15879e835281d 100644 --- a/src/Scripting/CSharp/CSharpScriptCompiler.cs +++ b/src/Scripting/CSharp/CSharpScriptCompiler.cs @@ -42,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, encoding: script.Options.Encoding); + var tree = SyntaxFactory.ParseSyntaxTree(script.Code, s_defaultOptions, script.Options.FilePath, encoding: script.Options.FileEncoding); string assemblyName, submissionTypeName; script.Builder.GenerateSubmissionId(out assemblyName, out submissionTypeName); diff --git a/src/Scripting/Core/Hosting/AssemblyLoader/DesktopAssemblyLoaderImpl.cs b/src/Scripting/Core/Hosting/AssemblyLoader/DesktopAssemblyLoaderImpl.cs index fb22f4f8b4b43..bb11704537152 100644 --- a/src/Scripting/Core/Hosting/AssemblyLoader/DesktopAssemblyLoaderImpl.cs +++ b/src/Scripting/Core/Hosting/AssemblyLoader/DesktopAssemblyLoaderImpl.cs @@ -31,7 +31,8 @@ public override Assembly LoadFromStream(Stream peStream, Stream pdbStream) byte[] peImage = new byte[peStream.Length]; peStream.TryReadAll(peImage, 0, peImage.Length); - if (pdbStream != null) { + if (pdbStream != null) + { byte[] pdbImage = new byte[pdbStream.Length]; pdbStream.TryReadAll(pdbImage, 0, pdbImage.Length); diff --git a/src/Scripting/Core/PublicAPI.Unshipped.txt b/src/Scripting/Core/PublicAPI.Unshipped.txt index a42ed1070fcb2..e63fc2dd659b6 100644 --- a/src/Scripting/Core/PublicAPI.Unshipped.txt +++ b/src/Scripting/Core/PublicAPI.Unshipped.txt @@ -1,5 +1,4 @@ -Microsoft.CodeAnalysis.Scripting.ScriptOptions.DebugInformationFormat.get -> Microsoft.CodeAnalysis.Emit.DebugInformationFormat? Microsoft.CodeAnalysis.Scripting.ScriptOptions.EmitDebugInformation.get -> bool -Microsoft.CodeAnalysis.Scripting.ScriptOptions.Encoding.get -> System.Text.Encoding -Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithDebugInformation(Microsoft.CodeAnalysis.Emit.DebugInformationFormat pdbFormat) -> Microsoft.CodeAnalysis.Scripting.ScriptOptions -Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithEncoding(System.Text.Encoding encoding) -> Microsoft.CodeAnalysis.Scripting.ScriptOptions \ No newline at end of file +Microsoft.CodeAnalysis.Scripting.ScriptOptions.FileEncoding.get -> System.Text.Encoding +Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithDebugInformation() -> Microsoft.CodeAnalysis.Scripting.ScriptOptions +Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithFileEncoding(System.Text.Encoding encoding) -> Microsoft.CodeAnalysis.Scripting.ScriptOptions \ No newline at end of file diff --git a/src/Scripting/Core/ScriptBuilder.cs b/src/Scripting/Core/ScriptBuilder.cs index 45d778e6cbc76..9c279a8bd9b42 100644 --- a/src/Scripting/Core/ScriptBuilder.cs +++ b/src/Scripting/Core/ScriptBuilder.cs @@ -76,7 +76,7 @@ internal Func> CreateExecutor(ScriptCompiler compiler, Comp ThrowIfAnyCompilationErrors(diagnostics, compiler.DiagnosticFormatter); diagnostics.Clear(); - var executor = Build(compilation, diagnostics, scriptOptions, cancellationToken); + var executor = Build(compilation, diagnostics, scriptOptions.EmitDebugInformation, cancellationToken); // emit can fail due to compilation errors or because there is nothing to emit: ThrowIfAnyCompilationErrors(diagnostics, compiler.DiagnosticFormatter); @@ -115,24 +115,25 @@ private static void ThrowIfAnyCompilationErrors(DiagnosticBag diagnostics, Diagn /// private Func> Build( Compilation compilation, - DiagnosticBag diagnostics, ScriptOptions scriptOptions, + DiagnosticBag diagnostics, + bool emitDebugInformation, CancellationToken cancellationToken) { var entryPoint = compilation.GetEntryPoint(cancellationToken); using (var peStream = new MemoryStream()) - using (var pdbStream = scriptOptions.EmitDebugInformation ? new MemoryStream() : null) + using (var pdbStreamOpt = emitDebugInformation ? new MemoryStream() : null) { var emitOptions = EmitOptions.Default; - if (scriptOptions.EmitDebugInformation) + if (emitDebugInformation) { - emitOptions = emitOptions.WithDebugInformationFormat(scriptOptions.DebugInformationFormat.Value); + emitOptions = emitOptions.WithDebugInformationFormat(PdbHelpers.GetPlatformSpecificDebugInformationFormat()); } var emitResult = compilation.Emit( peStream: peStream, - pdbStream: pdbStream, + pdbStream: pdbStreamOpt, xmlDocumentationStream: null, win32Resources: null, manifestResources: null, @@ -160,12 +161,12 @@ private Func> Build( peStream.Position = 0; - if (pdbStream != null) + if (pdbStreamOpt != null) { - pdbStream.Position = 0; + pdbStreamOpt.Position = 0; } - var assembly = _assemblyLoader.LoadAssemblyFromStream(peStream, pdbStream); + var assembly = _assemblyLoader.LoadAssemblyFromStream(peStream, pdbStreamOpt); var runtimeEntryPoint = GetEntryPointRuntimeMethod(entryPoint, assembly, cancellationToken); return runtimeEntryPoint.CreateDelegate>>(); diff --git a/src/Scripting/Core/ScriptCompiler.cs b/src/Scripting/Core/ScriptCompiler.cs index f86a208439bd9..2f0963825ab20 100644 --- a/src/Scripting/Core/ScriptCompiler.cs +++ b/src/Scripting/Core/ScriptCompiler.cs @@ -3,7 +3,6 @@ using System; using System.Text; using System.Threading; -using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Scripting diff --git a/src/Scripting/Core/ScriptOptions.cs b/src/Scripting/Core/ScriptOptions.cs index 2107507fb897a..fd9a2ebc5f1cf 100644 --- a/src/Scripting/Core/ScriptOptions.cs +++ b/src/Scripting/Core/ScriptOptions.cs @@ -7,12 +7,12 @@ using System.Linq; using System.Reflection; using System.Threading; +using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Scripting.Hosting; using System.Text; namespace Microsoft.CodeAnalysis.Scripting { - using Microsoft.CodeAnalysis.Emit; using static ParameterValidationHelpers; /// @@ -95,17 +95,12 @@ private static ImmutableArray GetDefaultMetadataReferences() /// /// Specifies whether debugging symbols should be emitted. /// - public bool EmitDebugInformation => DebugInformationFormat != null; + public bool EmitDebugInformation { get; private set; } = false; /// - /// to be used for debugging symbols if needed + /// Specifies the encoding to be used when debugging scripts loaded from a file, or that will be saved to a file for debugging purposes. /// - public DebugInformationFormat? DebugInformationFormat { get; private set; } = null; - - /// - /// Specifies the encoding to be used for the code. - /// - public Encoding Encoding { get; private set; } = Encoding.UTF8; + public Encoding FileEncoding { get; private set; } = Encoding.UTF8; /// /// The path to the script source if it originated from a file, empty otherwise. @@ -139,8 +134,8 @@ private ScriptOptions(ScriptOptions other) metadataResolver: other.MetadataResolver, sourceResolver: other.SourceResolver) { - DebugInformationFormat = other.DebugInformationFormat; - Encoding = other.Encoding; + EmitDebugInformation = other.EmitDebugInformation; + FileEncoding = other.FileEncoding; } // a reference to an assembly should by default be equivalent to #r, which applies recursive global alias: @@ -302,15 +297,15 @@ public ScriptOptions AddImports(params string[] imports) => AddImports((IEnumerable)imports); /// - /// Creates a new with specified . + /// Creates a new with debugging information enabled. /// - public ScriptOptions WithDebugInformation(DebugInformationFormat pdbFormat) => - new ScriptOptions(this) { DebugInformationFormat = pdbFormat }; + public ScriptOptions WithDebugInformation() => + new ScriptOptions(this) { EmitDebugInformation = true }; /// - /// Creates a new with specified . + /// Creates a new with specified . /// - public ScriptOptions WithEncoding(Encoding encoding) => - new ScriptOptions(this) { Encoding = encoding }; + public ScriptOptions WithFileEncoding(Encoding encoding) => + new ScriptOptions(this) { FileEncoding = encoding }; } } diff --git a/src/Scripting/Core/Scripting.csproj b/src/Scripting/Core/Scripting.csproj index bc0e554641f6a..1b13d51f2fd8b 100644 --- a/src/Scripting/Core/Scripting.csproj +++ b/src/Scripting/Core/Scripting.csproj @@ -108,6 +108,7 @@ + diff --git a/src/Scripting/Core/Utilities/PdbHelpers.cs b/src/Scripting/Core/Utilities/PdbHelpers.cs new file mode 100644 index 0000000000000..981bb9620cc0b --- /dev/null +++ b/src/Scripting/Core/Utilities/PdbHelpers.cs @@ -0,0 +1,22 @@ +// 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; + } + } +} diff --git a/src/Scripting/CoreTest/ScriptOptionsTests.cs b/src/Scripting/CoreTest/ScriptOptionsTests.cs index 96770c4d0e8cf..2213abdda705f 100644 --- a/src/Scripting/CoreTest/ScriptOptionsTests.cs +++ b/src/Scripting/CoreTest/ScriptOptionsTests.cs @@ -151,12 +151,10 @@ public void WithImports_Errors() } [Fact] - public void WithDebugInformation_SetsEmitDebugInformation_SetsDebugInformation() + public void WithDebugInformation_SetsEmitDebugInformation() { - var format = DebugInformationFormat.Pdb; - var options = ScriptOptions.Default.WithDebugInformation(format); + var options = ScriptOptions.Default.WithDebugInformation(); Assert.Equal(true, options.EmitDebugInformation); - Assert.Equal(format, options.DebugInformationFormat); } } } diff --git a/src/Workspaces/CSharpTest/CSharpServicesTest.csproj b/src/Workspaces/CSharpTest/CSharpServicesTest.csproj index e91e0be59a228..a5cb19eb5528a 100644 --- a/src/Workspaces/CSharpTest/CSharpServicesTest.csproj +++ b/src/Workspaces/CSharpTest/CSharpServicesTest.csproj @@ -76,7 +76,9 @@ - + + + From 74dfe7c2fa67fe7c6e9bc60e6e9f00f25b695663 Mon Sep 17 00:00:00 2001 From: filipw Date: Tue, 17 Jan 2017 14:15:41 +0100 Subject: [PATCH 07/20] added tests for script PDB --- src/Scripting/CSharpTest/ScriptTests.cs | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index f26c117ef0aec..dfc9c839e2472 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -687,6 +687,35 @@ public async Task LoadedFileWithVoidReturn() Assert.Equal(0, result); } + [Fact] + public async Task ScriptOptionsConfiguredForDebuggingResultInPdbEmitted() + { + try + { + var opts = ScriptOptions.Default.WithDebugInformation(); + var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); + } + catch (Exception ex) + { + // line information is only available when PDBs have been emitted + Assert.Contains(":line 1", ex.StackTrace); + } + } + + [Fact] + public async Task ScriptOptionsNotConfiguredForDebuggingResultInPdbNotEmitted() + { + try + { + var script = await CSharpScript.RunAsync("throw new System.Exception();", ScriptOptions.Default); + } + catch (Exception ex) + { + // line information is only available when PDBs have been emitted + Assert.DoesNotContain(":line 1", ex.StackTrace); + } + } + [WorkItem(12348, "https://github.com/dotnet/roslyn/issues/12348")] [Fact] public async Task StreamWithOffset() From c38b68f7a872cb84514ebd92151011fde2ea72dc Mon Sep 17 00:00:00 2001 From: filipw Date: Tue, 17 Jan 2017 15:02:35 +0100 Subject: [PATCH 08/20] better test verification for pdb --- src/Scripting/CSharpTest/ScriptTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index dfc9c839e2472..4f581c898b0f5 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -698,7 +698,7 @@ public async Task ScriptOptionsConfiguredForDebuggingResultInPdbEmitted() catch (Exception ex) { // line information is only available when PDBs have been emitted - Assert.Contains(":line 1", ex.StackTrace); + Assert.Contains("at Submission#0.<>d__0.MoveNext() in :line 1", ex.StackTrace); } } @@ -712,7 +712,7 @@ public async Task ScriptOptionsNotConfiguredForDebuggingResultInPdbNotEmitted() catch (Exception ex) { // line information is only available when PDBs have been emitted - Assert.DoesNotContain(":line 1", ex.StackTrace); + Assert.DoesNotContain("at Submission#0.<>d__0.MoveNext() in :line 1", ex.StackTrace); } } From a8be42983a97da20f7f43869df21a43460ed83e3 Mon Sep 17 00:00:00 2001 From: filipw Date: Tue, 17 Jan 2017 20:41:12 +0100 Subject: [PATCH 09/20] applied code review feedback --- src/Scripting/CSharpTest/ScriptTests.cs | 20 +++++++++++-- .../Hosting/CommandLine/CommandLineRunner.cs | 4 ++- src/Scripting/Core/PublicAPI.Unshipped.txt | 2 +- src/Scripting/Core/Script.cs | 2 +- src/Scripting/Core/ScriptBuilder.cs | 4 +-- src/Scripting/Core/ScriptOptions.cs | 26 +++++++++------- src/Scripting/CoreTest/ScriptOptionsTests.cs | 30 +++++++++++++++++-- 7 files changed, 67 insertions(+), 21 deletions(-) diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index 4f581c898b0f5..d99b94d9fee16 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -11,6 +11,7 @@ using Xunit; using System.IO; using System.Globalization; +using System.Text; namespace Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests { @@ -692,13 +693,13 @@ public async Task ScriptOptionsConfiguredForDebuggingResultInPdbEmitted() { try { - var opts = ScriptOptions.Default.WithDebugInformation(); + var opts = ScriptOptions.Default.WithEmitDebugInformation(true); var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); } catch (Exception ex) { // line information is only available when PDBs have been emitted - Assert.Contains("at Submission#0.<>d__0.MoveNext() in :line 1", ex.StackTrace); + Assert.Equal("at Submission#0.<>d__0.MoveNext() in :line 1", GetStackTraceLine(ex, 0)); } } @@ -712,7 +713,7 @@ public async Task ScriptOptionsNotConfiguredForDebuggingResultInPdbNotEmitted() catch (Exception ex) { // line information is only available when PDBs have been emitted - Assert.DoesNotContain("at Submission#0.<>d__0.MoveNext() in :line 1", ex.StackTrace); + Assert.Equal("at Submission#0.<>d__0.MoveNext()", GetStackTraceLine(ex, 0)); } } @@ -768,5 +769,18 @@ public override Stream OpenRead(string resolvedPath) return stream; } } + + private string GetStackTraceLine(Exception ex, int index) + { + if (ex == null || ex.StackTrace == null) return null; + + var stackTrace = ex.StackTrace?.Split(new[] { Environment.NewLine }, StringSplitOptions.None); + if (stackTrace.Length >= index) + { + return stackTrace[index].Trim(); + } + + return null; + } } } diff --git a/src/Scripting/Core/Hosting/CommandLine/CommandLineRunner.cs b/src/Scripting/Core/Hosting/CommandLine/CommandLineRunner.cs index fa74ffa592cba..8cf5c7083e86f 100644 --- a/src/Scripting/Core/Hosting/CommandLine/CommandLineRunner.cs +++ b/src/Scripting/Core/Hosting/CommandLine/CommandLineRunner.cs @@ -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) diff --git a/src/Scripting/Core/PublicAPI.Unshipped.txt b/src/Scripting/Core/PublicAPI.Unshipped.txt index e63fc2dd659b6..87bc32bb14351 100644 --- a/src/Scripting/Core/PublicAPI.Unshipped.txt +++ b/src/Scripting/Core/PublicAPI.Unshipped.txt @@ -1,4 +1,4 @@ Microsoft.CodeAnalysis.Scripting.ScriptOptions.EmitDebugInformation.get -> bool Microsoft.CodeAnalysis.Scripting.ScriptOptions.FileEncoding.get -> System.Text.Encoding -Microsoft.CodeAnalysis.Scripting.ScriptOptions.WithDebugInformation() -> Microsoft.CodeAnalysis.Scripting.ScriptOptions +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 diff --git a/src/Scripting/Core/Script.cs b/src/Scripting/Core/Script.cs index 1055b8f9b42ce..e5ce509bd7d70 100644 --- a/src/Scripting/Core/Script.cs +++ b/src/Scripting/Core/Script.cs @@ -325,7 +325,7 @@ private Func> GetExecutor(CancellationToken cancellationToken) { if (_lazyExecutor == null) { - Interlocked.CompareExchange(ref _lazyExecutor, Builder.CreateExecutor(Compiler, GetCompilation(), Options, cancellationToken), null); + Interlocked.CompareExchange(ref _lazyExecutor, Builder.CreateExecutor(Compiler, GetCompilation(), Options.EmitDebugInformation, cancellationToken), null); } return _lazyExecutor; diff --git a/src/Scripting/Core/ScriptBuilder.cs b/src/Scripting/Core/ScriptBuilder.cs index 9c279a8bd9b42..3705461e2c8d4 100644 --- a/src/Scripting/Core/ScriptBuilder.cs +++ b/src/Scripting/Core/ScriptBuilder.cs @@ -66,7 +66,7 @@ public int GenerateSubmissionId(out string assemblyName, out string typeName) } /// Compilation has errors. - internal Func> CreateExecutor(ScriptCompiler compiler, Compilation compilation, ScriptOptions scriptOptions, CancellationToken cancellationToken) + internal Func> CreateExecutor(ScriptCompiler compiler, Compilation compilation, bool emitDebugInformation, CancellationToken cancellationToken) { var diagnostics = DiagnosticBag.GetInstance(); try @@ -76,7 +76,7 @@ internal Func> CreateExecutor(ScriptCompiler compiler, Comp ThrowIfAnyCompilationErrors(diagnostics, compiler.DiagnosticFormatter); diagnostics.Clear(); - var executor = Build(compilation, diagnostics, scriptOptions.EmitDebugInformation, cancellationToken); + var executor = Build(compilation, diagnostics, emitDebugInformation, cancellationToken); // emit can fail due to compilation errors or because there is nothing to emit: ThrowIfAnyCompilationErrors(diagnostics, compiler.DiagnosticFormatter); diff --git a/src/Scripting/Core/ScriptOptions.cs b/src/Scripting/Core/ScriptOptions.cs index fd9a2ebc5f1cf..ed89fb5e52ee4 100644 --- a/src/Scripting/Core/ScriptOptions.cs +++ b/src/Scripting/Core/ScriptOptions.cs @@ -25,7 +25,9 @@ public sealed class ScriptOptions references: GetDefaultMetadataReferences(), namespaces: ImmutableArray.Empty, metadataResolver: RuntimeMetadataReferenceResolver.Default, - sourceResolver: SourceFileResolver.Default); + sourceResolver: SourceFileResolver.Default, + emitDebugInformation: false, + fileEncoding: null); private static ImmutableArray GetDefaultMetadataReferences() { @@ -100,7 +102,7 @@ private static ImmutableArray GetDefaultMetadataReferences() /// /// Specifies the encoding to be used when debugging scripts loaded from a file, or that will be saved to a file for debugging purposes. /// - public Encoding FileEncoding { get; private set; } = Encoding.UTF8; + public Encoding FileEncoding { get; private set; } /// /// The path to the script source if it originated from a file, empty otherwise. @@ -112,7 +114,9 @@ internal ScriptOptions( ImmutableArray references, ImmutableArray namespaces, MetadataReferenceResolver metadataResolver, - SourceReferenceResolver sourceResolver) + SourceReferenceResolver sourceResolver, + bool emitDebugInformation, + Encoding fileEncoding) { Debug.Assert(filePath != null); Debug.Assert(!references.IsDefault); @@ -125,6 +129,8 @@ internal ScriptOptions( Imports = namespaces; MetadataResolver = metadataResolver; SourceResolver = sourceResolver; + EmitDebugInformation = emitDebugInformation; + FileEncoding = fileEncoding; } private ScriptOptions(ScriptOptions other) @@ -132,10 +138,10 @@ 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) { - EmitDebugInformation = other.EmitDebugInformation; - FileEncoding = other.FileEncoding; } // a reference to an assembly should by default be equivalent to #r, which applies recursive global alias: @@ -299,13 +305,13 @@ public ScriptOptions AddImports(params string[] imports) => /// /// Creates a new with debugging information enabled. /// - public ScriptOptions WithDebugInformation() => - new ScriptOptions(this) { EmitDebugInformation = true }; + public ScriptOptions WithEmitDebugInformation(bool emitDebugInformation) => + emitDebugInformation == EmitDebugInformation ? this : new ScriptOptions(this) { EmitDebugInformation = emitDebugInformation }; /// /// Creates a new with specified . /// - public ScriptOptions WithFileEncoding(Encoding encoding) => - new ScriptOptions(this) { FileEncoding = encoding }; + public ScriptOptions WithFileEncoding(Encoding encoding) => + encoding == FileEncoding ? this : new ScriptOptions(this) { FileEncoding = encoding }; } } diff --git a/src/Scripting/CoreTest/ScriptOptionsTests.cs b/src/Scripting/CoreTest/ScriptOptionsTests.cs index 2213abdda705f..210921400f6d9 100644 --- a/src/Scripting/CoreTest/ScriptOptionsTests.cs +++ b/src/Scripting/CoreTest/ScriptOptionsTests.cs @@ -4,6 +4,7 @@ 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; @@ -150,11 +151,34 @@ public void WithImports_Errors() options.WithImports(".blah"); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void WithEmitDebugInformation_SetsEmitDebugInformation(bool emitDebugInformation) + { + var options = ScriptOptions.Default.WithEmitDebugInformation(emitDebugInformation); + Assert.Equal(emitDebugInformation, options.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 WithDebugInformation_SetsEmitDebugInformation() + public void WithFileEncoding_SameValueTwice_DoesNotCreateNewInstance() { - var options = ScriptOptions.Default.WithDebugInformation(); - Assert.Equal(true, options.EmitDebugInformation); + var options = ScriptOptions.Default.WithFileEncoding(Encoding.ASCII); + Assert.Same(options, options.WithFileEncoding(Encoding.ASCII)); } } } From d743344a5126f6ab8992017f8d0b77e3fd139bba Mon Sep 17 00:00:00 2001 From: filipw Date: Tue, 17 Jan 2017 21:23:02 +0100 Subject: [PATCH 10/20] better tests for scripting PDB generation --- .../CSharpTest/CSharpScriptingTest.csproj | 3 + src/Scripting/CSharpTest/ScriptTests.cs | 102 +++++++++++++++++- src/Scripting/CSharpTest/TestFiles/debug.csx | 1 + 3 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 src/Scripting/CSharpTest/TestFiles/debug.csx diff --git a/src/Scripting/CSharpTest/CSharpScriptingTest.csproj b/src/Scripting/CSharpTest/CSharpScriptingTest.csproj index 657a4b7ed78f2..f1a703bd5804c 100644 --- a/src/Scripting/CSharpTest/CSharpScriptingTest.csproj +++ b/src/Scripting/CSharpTest/CSharpScriptingTest.csproj @@ -75,6 +75,9 @@ + + Always + diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index d99b94d9fee16..ffe349d421405 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -689,11 +689,74 @@ public async Task LoadedFileWithVoidReturn() } [Fact] - public async Task ScriptOptionsConfiguredForDebuggingResultInPdbEmitted() + public async Task Pdb_CodeFromFile_WithEmitDebugInformation_WithoutFileEncoding_CompilationErrorException() { try { - var opts = ScriptOptions.Default.WithEmitDebugInformation(true); + var testFilePath = Path.Combine("TestFiles", "debug.csx"); + var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFilePath(testFilePath).WithFileEncoding(null); + var script = await CSharpScript.RunAsync(File.ReadAllText(testFilePath), opts); + } + catch (CompilationErrorException ex) + { + Assert.EndsWith("error CS8055: Cannot emit debug information for a source text without encoding.", ex.Message); + } + } + + [Fact] + public async Task Pdb_CodeFromFile_WithEmitDebugInformation_WithFileEncoding_ResultInPdbEmitted() + { + try + { + var testFilePath = Path.Combine("TestFiles", "debug.csx"); + var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFilePath(testFilePath).WithFileEncoding(Encoding.UTF8); + var script = await CSharpScript.RunAsync(File.ReadAllText(testFilePath), opts); + } + catch (Exception ex) + { + // line information is only available when PDBs have been emitted + Assert.EndsWith("debug.csx:line 1", GetStackTraceLine(ex, 0)); + } + } + + [Fact] + public async Task Pdb_CodeFromFile_WithoutEmitDebugInformation_WithoutFileEncoding_ResultInPdbNotEmitted() + { + try + { + var testFilePath = Path.Combine("TestFiles", "debug.csx"); + var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath(testFilePath).WithFileEncoding(null); + var script = await CSharpScript.RunAsync(File.ReadAllText(testFilePath), opts); + } + catch (Exception ex) + { + // line information is only available when PDBs have been emitted + Assert.Equal("at Submission#0.<>d__0.MoveNext()", GetStackTraceLine(ex, 0)); + } + } + + [Fact] + public async Task Pdb_CodeFromFile_WithoutEmitDebugInformation_WithFileEncoding_ResultInPdbNotEmitted() + { + try + { + var testFilePath = Path.Combine("TestFiles", "debug.csx"); + var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath(testFilePath).WithFileEncoding(Encoding.UTF8); + var script = await CSharpScript.RunAsync(File.ReadAllText(testFilePath), opts); + } + catch (Exception ex) + { + // line information is only available when PDBs have been emitted + Assert.Equal("at Submission#0.<>d__0.MoveNext()", GetStackTraceLine(ex, 0)); + } + } + + [Fact] + public async Task Pdb_InlineCode_WithEmitDebugInformation_WithoutFileEncoding_ResultInPdbEmitted() + { + try + { + var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFileEncoding(null); var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); } catch (Exception ex) @@ -704,11 +767,42 @@ public async Task ScriptOptionsConfiguredForDebuggingResultInPdbEmitted() } [Fact] - public async Task ScriptOptionsNotConfiguredForDebuggingResultInPdbNotEmitted() + public async Task Pdb_InlineCode_WithEmitDebugInformation_WithFileEncoding_ResultInPdbEmitted() { try { - var script = await CSharpScript.RunAsync("throw new System.Exception();", ScriptOptions.Default); + var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFileEncoding(Encoding.UTF8); + var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); + } + catch (Exception ex) + { + // line information is only available when PDBs have been emitted + Assert.Equal("at Submission#0.<>d__0.MoveNext() in :line 1", GetStackTraceLine(ex, 0)); + } + } + + [Fact] + public async Task Pdb_InlineCode_WithoutEmitDebugInformation_WithoutFileEncoding_ResultInPdbNotEmitted() + { + try + { + var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFileEncoding(null); + var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); + } + catch (Exception ex) + { + // line information is only available when PDBs have been emitted + Assert.Equal("at Submission#0.<>d__0.MoveNext()", GetStackTraceLine(ex, 0)); + } + } + + [Fact] + public async Task Pdb_InlineCode_WithoutEmitDebugInformation_WithFileEncoding_ResultInPdbNotEmitted() + { + try + { + var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFileEncoding(Encoding.UTF8); + var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); } catch (Exception ex) { diff --git a/src/Scripting/CSharpTest/TestFiles/debug.csx b/src/Scripting/CSharpTest/TestFiles/debug.csx new file mode 100644 index 0000000000000..3e87fc902558c --- /dev/null +++ b/src/Scripting/CSharpTest/TestFiles/debug.csx @@ -0,0 +1 @@ +throw new System.Exception(); \ No newline at end of file From 64bed321b44f4657592126de70559145239acad0 Mon Sep 17 00:00:00 2001 From: filipw Date: Tue, 17 Jan 2017 21:48:58 +0100 Subject: [PATCH 11/20] removed test file --- .../CSharpTest/CSharpScriptingTest.csproj | 3 --- src/Scripting/CSharpTest/ScriptTests.cs | 20 ++++++++----------- src/Scripting/CSharpTest/TestFiles/debug.csx | 1 - 3 files changed, 8 insertions(+), 16 deletions(-) delete mode 100644 src/Scripting/CSharpTest/TestFiles/debug.csx diff --git a/src/Scripting/CSharpTest/CSharpScriptingTest.csproj b/src/Scripting/CSharpTest/CSharpScriptingTest.csproj index f1a703bd5804c..657a4b7ed78f2 100644 --- a/src/Scripting/CSharpTest/CSharpScriptingTest.csproj +++ b/src/Scripting/CSharpTest/CSharpScriptingTest.csproj @@ -75,9 +75,6 @@ - - Always - diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index ffe349d421405..9a8b0df852489 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -693,9 +693,8 @@ public async Task Pdb_CodeFromFile_WithEmitDebugInformation_WithoutFileEncoding_ { try { - var testFilePath = Path.Combine("TestFiles", "debug.csx"); - var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFilePath(testFilePath).WithFileEncoding(null); - var script = await CSharpScript.RunAsync(File.ReadAllText(testFilePath), opts); + var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFilePath("debug.csx").WithFileEncoding(null); + var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); } catch (CompilationErrorException ex) { @@ -708,9 +707,8 @@ public async Task Pdb_CodeFromFile_WithEmitDebugInformation_WithFileEncoding_Res { try { - var testFilePath = Path.Combine("TestFiles", "debug.csx"); - var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFilePath(testFilePath).WithFileEncoding(Encoding.UTF8); - var script = await CSharpScript.RunAsync(File.ReadAllText(testFilePath), opts); + var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFilePath("debug.csx").WithFileEncoding(Encoding.UTF8); + var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); } catch (Exception ex) { @@ -724,9 +722,8 @@ public async Task Pdb_CodeFromFile_WithoutEmitDebugInformation_WithoutFileEncodi { try { - var testFilePath = Path.Combine("TestFiles", "debug.csx"); - var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath(testFilePath).WithFileEncoding(null); - var script = await CSharpScript.RunAsync(File.ReadAllText(testFilePath), opts); + var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath("debug.csx").WithFileEncoding(null); + var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); } catch (Exception ex) { @@ -740,9 +737,8 @@ public async Task Pdb_CodeFromFile_WithoutEmitDebugInformation_WithFileEncoding_ { try { - var testFilePath = Path.Combine("TestFiles", "debug.csx"); - var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath(testFilePath).WithFileEncoding(Encoding.UTF8); - var script = await CSharpScript.RunAsync(File.ReadAllText(testFilePath), opts); + var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath("debug.csx").WithFileEncoding(Encoding.UTF8); + var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); } catch (Exception ex) { diff --git a/src/Scripting/CSharpTest/TestFiles/debug.csx b/src/Scripting/CSharpTest/TestFiles/debug.csx deleted file mode 100644 index 3e87fc902558c..0000000000000 --- a/src/Scripting/CSharpTest/TestFiles/debug.csx +++ /dev/null @@ -1 +0,0 @@ -throw new System.Exception(); \ No newline at end of file From 0e95e93d84ff705b2b571574a7d64173bcda3796 Mon Sep 17 00:00:00 2001 From: filipw Date: Wed, 18 Jan 2017 00:27:44 +0100 Subject: [PATCH 12/20] added Stream-based overloads to Create / Continue methods of scripting --- src/Scripting/CSharp/CSharpScript.cs | 15 +++++++ src/Scripting/CSharp/CSharpScriptCompiler.cs | 2 +- src/Scripting/CSharp/PublicAPI.Unshipped.txt | 1 + src/Scripting/Core/PublicAPI.Unshipped.txt | 3 ++ src/Scripting/Core/Script.cs | 41 +++++++++++++++++--- 5 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/Scripting/CSharp/CSharpScript.cs b/src/Scripting/CSharp/CSharpScript.cs index 8354428b25d33..70ecfbdfec347 100644 --- a/src/Scripting/CSharp/CSharpScript.cs +++ b/src/Scripting/CSharp/CSharpScript.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Scripting; @@ -26,6 +27,20 @@ public static Script Create(string code, ScriptOptions options = null, Typ return Script.CreateInitialScript(CSharpScriptCompiler.Instance, code, options, globalsType, assemblyLoader); } + #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + /// + /// Create a new C# script. + /// + /// The representing the source code of the script. + /// The script options. + /// Type of global object. + /// Custom assembly loader. + /// The return type of the script + public static Script Create(Stream stream, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null) + { + return Script.CreateInitialScript(CSharpScriptCompiler.Instance, stream, options, globalsType, assemblyLoader); + } + /// /// Create a new C# script. /// diff --git a/src/Scripting/CSharp/CSharpScriptCompiler.cs b/src/Scripting/CSharp/CSharpScriptCompiler.cs index 15879e835281d..a0080d2f48215 100644 --- a/src/Scripting/CSharp/CSharpScriptCompiler.cs +++ b/src/Scripting/CSharp/CSharpScriptCompiler.cs @@ -42,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, encoding: script.Options.FileEncoding); + var tree = SyntaxFactory.ParseSyntaxTree(script.SourceText, s_defaultOptions, script.Options.FilePath); string assemblyName, submissionTypeName; script.Builder.GenerateSubmissionId(out assemblyName, out submissionTypeName); diff --git a/src/Scripting/CSharp/PublicAPI.Unshipped.txt b/src/Scripting/CSharp/PublicAPI.Unshipped.txt index e69de29bb2d1d..8d9166517f9d4 100644 --- a/src/Scripting/CSharp/PublicAPI.Unshipped.txt +++ b/src/Scripting/CSharp/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +static Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.Create(System.IO.Stream stream, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null, System.Type globalsType = null, Microsoft.CodeAnalysis.Scripting.Hosting.InteractiveAssemblyLoader assemblyLoader = null) -> Microsoft.CodeAnalysis.Scripting.Script \ No newline at end of file diff --git a/src/Scripting/Core/PublicAPI.Unshipped.txt b/src/Scripting/Core/PublicAPI.Unshipped.txt index 87bc32bb14351..75d021bd06f27 100644 --- a/src/Scripting/Core/PublicAPI.Unshipped.txt +++ b/src/Scripting/Core/PublicAPI.Unshipped.txt @@ -1,3 +1,6 @@ +Microsoft.CodeAnalysis.Scripting.Script.ContinueWith(System.IO.Stream codeStream, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script +Microsoft.CodeAnalysis.Scripting.Script.ContinueWith(System.IO.Stream codeStream, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script +Microsoft.CodeAnalysis.Scripting.Script.SourceText.get -> Microsoft.CodeAnalysis.Text.SourceText 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 diff --git a/src/Scripting/Core/Script.cs b/src/Scripting/Core/Script.cs index e5ce509bd7d70..7c8637d3c1f19 100644 --- a/src/Scripting/Core/Script.cs +++ b/src/Scripting/Core/Script.cs @@ -12,6 +12,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 +29,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,7 +39,7 @@ internal Script(ScriptCompiler compiler, ScriptBuilder builder, string code, Scr Compiler = compiler; Builder = builder; Previous = previousOpt; - Code = code; + SourceText = sourceText; Options = options; GlobalsType = globalsTypeOpt; } @@ -47,6 +49,11 @@ internal static Script CreateInitialScript(ScriptCompiler compiler, string return new Script(compiler, new ScriptBuilder(assemblyLoaderOpt ?? new InteractiveAssemblyLoader()), codeOpt ?? "", optionsOpt ?? ScriptOptions.Default, globalsTypeOpt, previousOpt: null); } + internal static Script CreateInitialScript(ScriptCompiler compiler, Stream codeStream, ScriptOptions optionsOpt, Type globalsTypeOpt, InteractiveAssemblyLoader assemblyLoaderOpt) + { + return new Script(compiler, new ScriptBuilder(assemblyLoaderOpt ?? new InteractiveAssemblyLoader()), codeStream, optionsOpt ?? ScriptOptions.Default, globalsTypeOpt, previousOpt: null); + } + /// /// A script that will run first when this script is run. /// Any declarations made in the previous script can be referenced in this script. @@ -62,7 +69,12 @@ internal static Script CreateInitialScript(ScriptCompiler compiler, string /// /// The source code of the script. /// - public string Code { get; } + public string Code => SourceText.ToString(); + + /// + /// The of the script. + /// + public SourceText SourceText { get; } /// /// The type of an object whose members can be accessed by the script as global variables. @@ -86,12 +98,26 @@ internal static Script CreateInitialScript(ScriptCompiler compiler, string public Script ContinueWith(string code, ScriptOptions options = null) => ContinueWith(code, options); + #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + /// + /// Continues the script with given representing code. + /// + public Script ContinueWith(Stream codeStream, ScriptOptions options = null) => + ContinueWith(codeStream, options); + /// /// Continues the script with given code snippet. /// public Script ContinueWith(string code, ScriptOptions options = null) => new Script(Compiler, Builder, code ?? "", options ?? InheritOptions(Options), GlobalsType, this); + #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + /// + /// Continues the script with given representing code. + /// + public Script ContinueWith(Stream codeStream, ScriptOptions options = null) => + new Script(Compiler, Builder, codeStream, options ?? InheritOptions(Options), GlobalsType, this); + private static ScriptOptions InheritOptions(ScriptOptions previous) { // don't inherit references or imports, they have already been applied: @@ -279,7 +305,12 @@ public sealed class Script : Script private Func> _lazyExecutor; internal Script(ScriptCompiler compiler, ScriptBuilder builder, string code, ScriptOptions options, Type globalsTypeOpt, Script previousOpt) - : base(compiler, builder, code, options, globalsTypeOpt, previousOpt) + : base(compiler, builder, SourceText.From(code, options.FileEncoding), options, globalsTypeOpt, previousOpt) + { + } + + internal Script(ScriptCompiler compiler, ScriptBuilder builder, Stream codeStream, ScriptOptions options, Type globalsTypeOpt, Script previousOpt) + : base(compiler, builder, SourceText.From(codeStream, options.FileEncoding), options, globalsTypeOpt, previousOpt) { } From 2710583cd046758184cdf13771aa94f61d722370 Mon Sep 17 00:00:00 2001 From: filipw Date: Wed, 18 Jan 2017 00:38:23 +0100 Subject: [PATCH 13/20] removed usages of Code in favor of SourceText, renamed codeStream -> stream --- src/Scripting/CSharpTest/ScriptTests.cs | 8 ++++++++ src/Scripting/Core/PublicAPI.Unshipped.txt | 4 ++-- src/Scripting/Core/Script.cs | 23 +++++++++++++--------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index 9a8b0df852489..c16bdd078761f 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -38,6 +38,14 @@ public async Task TestGetCompilation() Assert.Equal(state.Script.Code, compilation.SyntaxTrees.First().GetText().ToString()); } + [Fact] + public async Task TestGetCompilation_SourceText() + { + 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() { diff --git a/src/Scripting/Core/PublicAPI.Unshipped.txt b/src/Scripting/Core/PublicAPI.Unshipped.txt index 75d021bd06f27..81784da4fc3c6 100644 --- a/src/Scripting/Core/PublicAPI.Unshipped.txt +++ b/src/Scripting/Core/PublicAPI.Unshipped.txt @@ -1,5 +1,5 @@ -Microsoft.CodeAnalysis.Scripting.Script.ContinueWith(System.IO.Stream codeStream, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script -Microsoft.CodeAnalysis.Scripting.Script.ContinueWith(System.IO.Stream codeStream, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script +Microsoft.CodeAnalysis.Scripting.Script.ContinueWith(System.IO.Stream stream, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script +Microsoft.CodeAnalysis.Scripting.Script.ContinueWith(System.IO.Stream stream, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script Microsoft.CodeAnalysis.Scripting.Script.SourceText.get -> Microsoft.CodeAnalysis.Text.SourceText Microsoft.CodeAnalysis.Scripting.ScriptOptions.EmitDebugInformation.get -> bool Microsoft.CodeAnalysis.Scripting.ScriptOptions.FileEncoding.get -> System.Text.Encoding diff --git a/src/Scripting/Core/Script.cs b/src/Scripting/Core/Script.cs index 7c8637d3c1f19..43f9b43744726 100644 --- a/src/Scripting/Core/Script.cs +++ b/src/Scripting/Core/Script.cs @@ -49,9 +49,9 @@ internal static Script CreateInitialScript(ScriptCompiler compiler, string return new Script(compiler, new ScriptBuilder(assemblyLoaderOpt ?? new InteractiveAssemblyLoader()), codeOpt ?? "", optionsOpt ?? ScriptOptions.Default, globalsTypeOpt, previousOpt: null); } - internal static Script CreateInitialScript(ScriptCompiler compiler, Stream codeStream, ScriptOptions optionsOpt, Type globalsTypeOpt, InteractiveAssemblyLoader assemblyLoaderOpt) + internal static Script CreateInitialScript(ScriptCompiler compiler, Stream stream, ScriptOptions optionsOpt, Type globalsTypeOpt, InteractiveAssemblyLoader assemblyLoaderOpt) { - return new Script(compiler, new ScriptBuilder(assemblyLoaderOpt ?? new InteractiveAssemblyLoader()), codeStream, optionsOpt ?? ScriptOptions.Default, globalsTypeOpt, previousOpt: null); + return new Script(compiler, new ScriptBuilder(assemblyLoaderOpt ?? new InteractiveAssemblyLoader()), stream, optionsOpt ?? ScriptOptions.Default, globalsTypeOpt, previousOpt: null); } /// @@ -102,8 +102,8 @@ public Script ContinueWith(string code, ScriptOptions options = null) => /// /// Continues the script with given representing code. /// - public Script ContinueWith(Stream codeStream, ScriptOptions options = null) => - ContinueWith(codeStream, options); + public Script ContinueWith(Stream stream, ScriptOptions options = null) => + ContinueWith(stream, options); /// /// Continues the script with given code snippet. @@ -115,8 +115,8 @@ public Script ContinueWith(string code, ScriptOptions options /// /// Continues the script with given representing code. /// - public Script ContinueWith(Stream codeStream, ScriptOptions options = null) => - new Script(Compiler, Builder, codeStream, options ?? InheritOptions(Options), GlobalsType, this); + public Script ContinueWith(Stream stream, ScriptOptions options = null) => + new Script(Compiler, Builder, stream, options ?? InheritOptions(Options), GlobalsType, this); private static ScriptOptions InheritOptions(ScriptOptions previous) { @@ -309,8 +309,13 @@ internal Script(ScriptCompiler compiler, ScriptBuilder builder, string code, Scr { } - internal Script(ScriptCompiler compiler, ScriptBuilder builder, Stream codeStream, ScriptOptions options, Type globalsTypeOpt, Script previousOpt) - : base(compiler, builder, SourceText.From(codeStream, options.FileEncoding), options, globalsTypeOpt, previousOpt) + internal Script(ScriptCompiler compiler, ScriptBuilder builder, Stream stream, ScriptOptions options, Type globalsTypeOpt, Script previousOpt) + : base(compiler, builder, SourceText.From(stream, options.FileEncoding), options, globalsTypeOpt, previousOpt) + { + } + + internal Script(ScriptCompiler compiler, ScriptBuilder builder, SourceText sourceText, ScriptOptions options, Type globalsTypeOpt, Script previousOpt) + : base(compiler, builder, sourceText, options, globalsTypeOpt, previousOpt) { } @@ -318,7 +323,7 @@ internal Script(ScriptCompiler compiler, ScriptBuilder builder, Stream codeStrea public new Script WithOptions(ScriptOptions options) { - return (options == Options) ? this : new Script(Compiler, Builder, Code, options, GlobalsType, Previous); + return (options == Options) ? this : new Script(Compiler, Builder, SourceText, options, GlobalsType, Previous); } internal override Script WithOptionsInternal(ScriptOptions options) => WithOptions(options); From 78d2ed42eed02ee953b450518ae50fdad36c2a1a Mon Sep 17 00:00:00 2001 From: filipw Date: Wed, 18 Jan 2017 00:45:25 +0100 Subject: [PATCH 14/20] added a new Create Script overload --- src/Scripting/CSharp/CSharpScript.cs | 13 +++++++++++++ src/Scripting/CSharp/PublicAPI.Unshipped.txt | 1 + src/Scripting/CSharpTest/ScriptTests.cs | 18 +++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/Scripting/CSharp/CSharpScript.cs b/src/Scripting/CSharp/CSharpScript.cs index 70ecfbdfec347..1e69c689db140 100644 --- a/src/Scripting/CSharp/CSharpScript.cs +++ b/src/Scripting/CSharp/CSharpScript.cs @@ -53,6 +53,19 @@ public static Script Create(string code, ScriptOptions options = null, T return Create(code, options, globalsType, assemblyLoader); } + #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + /// + /// Create a new C# script. + /// + /// The representing the source code of the script. + /// The script options. + /// Type of global object. + /// Custom assembly loader. + public static Script Create(Stream stream, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null) + { + return Create(stream, options, globalsType, assemblyLoader); + } + /// /// Run a C# script. /// diff --git a/src/Scripting/CSharp/PublicAPI.Unshipped.txt b/src/Scripting/CSharp/PublicAPI.Unshipped.txt index 8d9166517f9d4..8b6efd197e2dc 100644 --- a/src/Scripting/CSharp/PublicAPI.Unshipped.txt +++ b/src/Scripting/CSharp/PublicAPI.Unshipped.txt @@ -1 +1,2 @@ +static Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.Create(System.IO.Stream stream, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null, System.Type globalsType = null, Microsoft.CodeAnalysis.Scripting.Hosting.InteractiveAssemblyLoader assemblyLoader = null) -> Microsoft.CodeAnalysis.Scripting.Script static Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.Create(System.IO.Stream stream, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null, System.Type globalsType = null, Microsoft.CodeAnalysis.Scripting.Hosting.InteractiveAssemblyLoader assemblyLoader = null) -> Microsoft.CodeAnalysis.Scripting.Script \ No newline at end of file diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index c16bdd078761f..f1c89a3df6c70 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -30,6 +30,13 @@ public void TestCreateScript() Assert.Equal("1 + 2", script.Code); } + [Fact] + public void TestCreateFromStreamScript() + { + var script = CSharpScript.Create(new MemoryStream(Encoding.UTF8.GetBytes("1 + 2"))); + Assert.Equal("1 + 2", script.Code); + } + [Fact] public async Task TestGetCompilation() { @@ -39,7 +46,7 @@ public async Task TestGetCompilation() } [Fact] - public async Task TestGetCompilation_SourceText() + public async Task TestGetCompilationSourceText() { var state = await CSharpScript.RunAsync("1 + 2", globals: new ScriptTests()); var compilation = state.Script.GetCompilation(); @@ -85,6 +92,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() { From f7d2e80cbf0c4024570570627d6a601601be6835 Mon Sep 17 00:00:00 2001 From: filipw Date: Wed, 18 Jan 2017 01:14:47 +0100 Subject: [PATCH 15/20] added exceptions to docs, renamed stream => code --- src/Scripting/CSharp/CSharpScript.cs | 16 ++++++++++------ src/Scripting/CSharp/PublicAPI.Unshipped.txt | 4 ++-- src/Scripting/Core/PublicAPI.Unshipped.txt | 4 ++-- src/Scripting/Core/Script.cs | 20 ++++++++++++-------- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/Scripting/CSharp/CSharpScript.cs b/src/Scripting/CSharp/CSharpScript.cs index 1e69c689db140..58c82d339010d 100644 --- a/src/Scripting/CSharp/CSharpScript.cs +++ b/src/Scripting/CSharp/CSharpScript.cs @@ -31,14 +31,16 @@ public static Script Create(string code, ScriptOptions options = null, Typ /// /// Create a new C# script. /// - /// The representing the source code of the script. + /// The representing the source code of the script. /// The script options. /// Type of global object. /// Custom assembly loader. /// The return type of the script - public static Script Create(Stream stream, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null) + /// Stream is null. + /// Stream is not readable or seekable. + public static Script Create(Stream code, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null) { - return Script.CreateInitialScript(CSharpScriptCompiler.Instance, stream, options, globalsType, assemblyLoader); + return Script.CreateInitialScript(CSharpScriptCompiler.Instance, code, options, globalsType, assemblyLoader); } /// @@ -57,13 +59,15 @@ public static Script Create(string code, ScriptOptions options = null, T /// /// Create a new C# script. /// - /// The representing the source code of the script. + /// The representing the source code of the script. /// The script options. /// Type of global object. /// Custom assembly loader. - public static Script Create(Stream stream, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null) + /// Stream is null. + /// Stream is not readable or seekable. + public static Script Create(Stream code, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null) { - return Create(stream, options, globalsType, assemblyLoader); + return Create(code, options, globalsType, assemblyLoader); } /// diff --git a/src/Scripting/CSharp/PublicAPI.Unshipped.txt b/src/Scripting/CSharp/PublicAPI.Unshipped.txt index 8b6efd197e2dc..3b3b39acca0ca 100644 --- a/src/Scripting/CSharp/PublicAPI.Unshipped.txt +++ b/src/Scripting/CSharp/PublicAPI.Unshipped.txt @@ -1,2 +1,2 @@ -static Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.Create(System.IO.Stream stream, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null, System.Type globalsType = null, Microsoft.CodeAnalysis.Scripting.Hosting.InteractiveAssemblyLoader assemblyLoader = null) -> Microsoft.CodeAnalysis.Scripting.Script -static Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.Create(System.IO.Stream stream, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null, System.Type globalsType = null, Microsoft.CodeAnalysis.Scripting.Hosting.InteractiveAssemblyLoader assemblyLoader = null) -> Microsoft.CodeAnalysis.Scripting.Script \ No newline at end of file +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 +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 \ No newline at end of file diff --git a/src/Scripting/Core/PublicAPI.Unshipped.txt b/src/Scripting/Core/PublicAPI.Unshipped.txt index 81784da4fc3c6..bf60c2a3b9f14 100644 --- a/src/Scripting/Core/PublicAPI.Unshipped.txt +++ b/src/Scripting/Core/PublicAPI.Unshipped.txt @@ -1,5 +1,5 @@ -Microsoft.CodeAnalysis.Scripting.Script.ContinueWith(System.IO.Stream stream, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script -Microsoft.CodeAnalysis.Scripting.Script.ContinueWith(System.IO.Stream stream, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script +Microsoft.CodeAnalysis.Scripting.Script.ContinueWith(System.IO.Stream code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script +Microsoft.CodeAnalysis.Scripting.Script.ContinueWith(System.IO.Stream code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script Microsoft.CodeAnalysis.Scripting.Script.SourceText.get -> Microsoft.CodeAnalysis.Text.SourceText Microsoft.CodeAnalysis.Scripting.ScriptOptions.EmitDebugInformation.get -> bool Microsoft.CodeAnalysis.Scripting.ScriptOptions.FileEncoding.get -> System.Text.Encoding diff --git a/src/Scripting/Core/Script.cs b/src/Scripting/Core/Script.cs index 43f9b43744726..abbfaab57657e 100644 --- a/src/Scripting/Core/Script.cs +++ b/src/Scripting/Core/Script.cs @@ -49,9 +49,9 @@ internal static Script CreateInitialScript(ScriptCompiler compiler, string return new Script(compiler, new ScriptBuilder(assemblyLoaderOpt ?? new InteractiveAssemblyLoader()), codeOpt ?? "", optionsOpt ?? ScriptOptions.Default, globalsTypeOpt, previousOpt: null); } - internal static Script CreateInitialScript(ScriptCompiler compiler, Stream stream, ScriptOptions optionsOpt, Type globalsTypeOpt, InteractiveAssemblyLoader assemblyLoaderOpt) + internal static Script CreateInitialScript(ScriptCompiler compiler, Stream code, ScriptOptions optionsOpt, Type globalsTypeOpt, InteractiveAssemblyLoader assemblyLoaderOpt) { - return new Script(compiler, new ScriptBuilder(assemblyLoaderOpt ?? new InteractiveAssemblyLoader()), stream, optionsOpt ?? ScriptOptions.Default, globalsTypeOpt, previousOpt: null); + return new Script(compiler, new ScriptBuilder(assemblyLoaderOpt ?? new InteractiveAssemblyLoader()), code, optionsOpt ?? ScriptOptions.Default, globalsTypeOpt, previousOpt: null); } /// @@ -102,8 +102,10 @@ public Script ContinueWith(string code, ScriptOptions options = null) => /// /// Continues the script with given representing code. /// - public Script ContinueWith(Stream stream, ScriptOptions options = null) => - ContinueWith(stream, options); + /// Stream is null. + /// Stream is not readable or seekable. + public Script ContinueWith(Stream code, ScriptOptions options = null) => + ContinueWith(code, options); /// /// Continues the script with given code snippet. @@ -115,8 +117,10 @@ public Script ContinueWith(string code, ScriptOptions options /// /// Continues the script with given representing code. /// - public Script ContinueWith(Stream stream, ScriptOptions options = null) => - new Script(Compiler, Builder, stream, options ?? InheritOptions(Options), GlobalsType, this); + /// Stream is null. + /// Stream is not readable or seekable. + public Script ContinueWith(Stream code, ScriptOptions options = null) => + new Script(Compiler, Builder, code, options ?? InheritOptions(Options), GlobalsType, this); private static ScriptOptions InheritOptions(ScriptOptions previous) { @@ -309,8 +313,8 @@ internal Script(ScriptCompiler compiler, ScriptBuilder builder, string code, Scr { } - internal Script(ScriptCompiler compiler, ScriptBuilder builder, Stream stream, ScriptOptions options, Type globalsTypeOpt, Script previousOpt) - : base(compiler, builder, SourceText.From(stream, options.FileEncoding), options, globalsTypeOpt, previousOpt) + internal Script(ScriptCompiler compiler, ScriptBuilder builder, Stream code, ScriptOptions options, Type globalsTypeOpt, Script previousOpt) + : base(compiler, builder, SourceText.From(code, options.FileEncoding), options, globalsTypeOpt, previousOpt) { } From f47a9b2ecda4f807535c4e3edcc2dee5536ab6a2 Mon Sep 17 00:00:00 2001 From: filipw Date: Wed, 18 Jan 2017 08:48:54 +0100 Subject: [PATCH 16/20] changes according to @tmat comments --- src/Scripting/CSharp/CSharpScript.cs | 8 +++--- .../Hosting/CommandLine/CommandLineRunner.cs | 6 ++-- src/Scripting/Core/PublicAPI.Unshipped.txt | 1 - src/Scripting/Core/Script.cs | 28 ++++--------------- .../VisualBasic/VisualBasicScript.vb | 3 +- 5 files changed, 15 insertions(+), 31 deletions(-) diff --git a/src/Scripting/CSharp/CSharpScript.cs b/src/Scripting/CSharp/CSharpScript.cs index 58c82d339010d..3576314095d45 100644 --- a/src/Scripting/CSharp/CSharpScript.cs +++ b/src/Scripting/CSharp/CSharpScript.cs @@ -1,4 +1,5 @@ // 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; @@ -6,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Scripting; using Microsoft.CodeAnalysis.Scripting.Hosting; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Scripting { @@ -24,10 +26,9 @@ public static class CSharpScript /// The return type of the script public static Script Create(string code, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null) { - return Script.CreateInitialScript(CSharpScriptCompiler.Instance, code, options, globalsType, assemblyLoader); + return Script.CreateInitialScript(CSharpScriptCompiler.Instance, SourceText.From(code, options?.FileEncoding), options, globalsType, assemblyLoader); } - #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters /// /// Create a new C# script. /// @@ -40,7 +41,7 @@ public static Script Create(string code, ScriptOptions options = null, Typ /// Stream is not readable or seekable. public static Script Create(Stream code, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null) { - return Script.CreateInitialScript(CSharpScriptCompiler.Instance, code, options, globalsType, assemblyLoader); + return Script.CreateInitialScript(CSharpScriptCompiler.Instance, SourceText.From(code, options?.FileEncoding), options, globalsType, assemblyLoader); } /// @@ -55,7 +56,6 @@ public static Script Create(string code, ScriptOptions options = null, T return Create(code, options, globalsType, assemblyLoader); } - #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters /// /// Create a new C# script. /// diff --git a/src/Scripting/Core/Hosting/CommandLine/CommandLineRunner.cs b/src/Scripting/Core/Hosting/CommandLine/CommandLineRunner.cs index 8cf5c7083e86f..6a1a5ffb3f2d9 100644 --- a/src/Scripting/Core/Hosting/CommandLine/CommandLineRunner.cs +++ b/src/Scripting/Core/Hosting/CommandLine/CommandLineRunner.cs @@ -178,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(_scriptCompiler, code, options, globals.GetType(), assemblyLoaderOpt: null); + var script = Script.CreateInitialScript(_scriptCompiler, SourceText.From(code ?? string.Empty), options, globals.GetType(), assemblyLoaderOpt: null); try { return script.RunAsync(globals, cancellationToken).Result.ReturnValue; @@ -199,7 +199,7 @@ private void RunInteractiveLoop(ScriptOptions options, string initialScriptCodeO if (initialScriptCodeOpt != null) { - var script = Script.CreateInitialScript(_scriptCompiler, initialScriptCodeOpt, options, globals.GetType(), assemblyLoaderOpt: null); + var script = Script.CreateInitialScript(_scriptCompiler, SourceText.From(initialScriptCodeOpt ?? string.Empty), options, globals.GetType(), assemblyLoaderOpt: null); BuildAndRun(script, globals, ref state, ref options, displayResult: false, cancellationToken: cancellationToken); } @@ -251,7 +251,7 @@ private void RunInteractiveLoop(ScriptOptions options, string initialScriptCodeO Script newScript; if (state == null) { - newScript = Script.CreateInitialScript(_scriptCompiler, code, options, globals.GetType(), assemblyLoaderOpt: null); + newScript = Script.CreateInitialScript(_scriptCompiler, SourceText.From(code ?? string.Empty), options, globals.GetType(), assemblyLoaderOpt: null); } else { diff --git a/src/Scripting/Core/PublicAPI.Unshipped.txt b/src/Scripting/Core/PublicAPI.Unshipped.txt index bf60c2a3b9f14..b298e40d84f19 100644 --- a/src/Scripting/Core/PublicAPI.Unshipped.txt +++ b/src/Scripting/Core/PublicAPI.Unshipped.txt @@ -1,6 +1,5 @@ Microsoft.CodeAnalysis.Scripting.Script.ContinueWith(System.IO.Stream code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script Microsoft.CodeAnalysis.Scripting.Script.ContinueWith(System.IO.Stream code, Microsoft.CodeAnalysis.Scripting.ScriptOptions options = null) -> Microsoft.CodeAnalysis.Scripting.Script -Microsoft.CodeAnalysis.Scripting.Script.SourceText.get -> Microsoft.CodeAnalysis.Text.SourceText 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 diff --git a/src/Scripting/Core/Script.cs b/src/Scripting/Core/Script.cs index abbfaab57657e..cefdc65bef634 100644 --- a/src/Scripting/Core/Script.cs +++ b/src/Scripting/Core/Script.cs @@ -1,4 +1,5 @@ // 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; @@ -44,14 +45,9 @@ internal Script(ScriptCompiler compiler, ScriptBuilder builder, SourceText sourc GlobalsType = globalsTypeOpt; } - internal static Script CreateInitialScript(ScriptCompiler compiler, string codeOpt, ScriptOptions optionsOpt, Type globalsTypeOpt, InteractiveAssemblyLoader assemblyLoaderOpt) + internal static Script CreateInitialScript(ScriptCompiler compiler, SourceText sourceText, ScriptOptions optionsOpt, Type globalsTypeOpt, InteractiveAssemblyLoader assemblyLoaderOpt) { - return new Script(compiler, new ScriptBuilder(assemblyLoaderOpt ?? new InteractiveAssemblyLoader()), codeOpt ?? "", optionsOpt ?? ScriptOptions.Default, globalsTypeOpt, previousOpt: null); - } - - internal static Script CreateInitialScript(ScriptCompiler compiler, Stream code, ScriptOptions optionsOpt, Type globalsTypeOpt, InteractiveAssemblyLoader assemblyLoaderOpt) - { - return new Script(compiler, new ScriptBuilder(assemblyLoaderOpt ?? new InteractiveAssemblyLoader()), code, optionsOpt ?? ScriptOptions.Default, globalsTypeOpt, previousOpt: null); + return new Script(compiler, new ScriptBuilder(assemblyLoaderOpt ?? new InteractiveAssemblyLoader()), sourceText, optionsOpt ?? ScriptOptions.Default, globalsTypeOpt, previousOpt: null); } /// @@ -74,7 +70,7 @@ internal static Script CreateInitialScript(ScriptCompiler compiler, Stream /// /// The of the script. /// - public SourceText SourceText { get; } + internal SourceText SourceText { get; } /// /// The type of an object whose members can be accessed by the script as global variables. @@ -98,7 +94,6 @@ internal static Script CreateInitialScript(ScriptCompiler compiler, Stream public Script ContinueWith(string code, ScriptOptions options = null) => ContinueWith(code, options); - #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters /// /// Continues the script with given representing code. /// @@ -111,16 +106,15 @@ public Script ContinueWith(Stream code, ScriptOptions options = null) => /// Continues the script with given code snippet. /// public Script ContinueWith(string code, ScriptOptions options = null) => - new Script(Compiler, Builder, code ?? "", options ?? InheritOptions(Options), GlobalsType, this); + new Script(Compiler, Builder, SourceText.From(code ?? "", options?.FileEncoding ?? Options.FileEncoding), options ?? InheritOptions(Options), GlobalsType, this); - #pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters /// /// Continues the script with given representing code. /// /// Stream is null. /// Stream is not readable or seekable. public Script ContinueWith(Stream code, ScriptOptions options = null) => - new Script(Compiler, Builder, code, options ?? InheritOptions(Options), GlobalsType, this); + new Script(Compiler, Builder, SourceText.From(code, options?.FileEncoding ?? Options.FileEncoding), options ?? InheritOptions(Options), GlobalsType, this); private static ScriptOptions InheritOptions(ScriptOptions previous) { @@ -308,16 +302,6 @@ public sealed class Script : Script private ImmutableArray> _lazyPrecedingExecutors; private Func> _lazyExecutor; - internal Script(ScriptCompiler compiler, ScriptBuilder builder, string code, ScriptOptions options, Type globalsTypeOpt, Script previousOpt) - : base(compiler, builder, SourceText.From(code, options.FileEncoding), options, globalsTypeOpt, previousOpt) - { - } - - internal Script(ScriptCompiler compiler, ScriptBuilder builder, Stream code, ScriptOptions options, Type globalsTypeOpt, Script previousOpt) - : base(compiler, builder, SourceText.From(code, options.FileEncoding), options, globalsTypeOpt, previousOpt) - { - } - internal Script(ScriptCompiler compiler, ScriptBuilder builder, SourceText sourceText, ScriptOptions options, Type globalsTypeOpt, Script previousOpt) : base(compiler, builder, sourceText, options, globalsTypeOpt, previousOpt) { diff --git a/src/Scripting/VisualBasic/VisualBasicScript.vb b/src/Scripting/VisualBasic/VisualBasicScript.vb index f30778d3d21ec..18da2d580e2ee 100644 --- a/src/Scripting/VisualBasic/VisualBasicScript.vb +++ b/src/Scripting/VisualBasic/VisualBasicScript.vb @@ -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 ''' From 739b80552feb45e5ac75ed37e53542b2fb289300 Mon Sep 17 00:00:00 2001 From: filipw Date: Thu, 19 Jan 2017 14:30:43 +0100 Subject: [PATCH 17/20] added some extra tests for PDB via the Create(Stream) path --- src/Scripting/CSharpTest/ScriptTests.cs | 142 +++++++++++------------- 1 file changed, 64 insertions(+), 78 deletions(-) diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index f1c89a3df6c70..9250e9b0a6825 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -713,7 +713,7 @@ public async Task LoadedFileWithVoidReturn() } [Fact] - public async Task Pdb_CodeFromFile_WithEmitDebugInformation_WithoutFileEncoding_CompilationErrorException() + public async Task Pdb_CreateFromString_CodeFromFile_WithEmitDebugInformation_WithoutFileEncoding_CompilationErrorException() { try { @@ -727,108 +727,80 @@ public async Task Pdb_CodeFromFile_WithEmitDebugInformation_WithoutFileEncoding_ } [Fact] - public async Task Pdb_CodeFromFile_WithEmitDebugInformation_WithFileEncoding_ResultInPdbEmitted() + public Task Pdb_CreateFromString_CodeFromFile_WithEmitDebugInformation_WithFileEncoding_ResultInPdbEmitted() { - try - { - var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFilePath("debug.csx").WithFileEncoding(Encoding.UTF8); - var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); - } - catch (Exception ex) - { - // line information is only available when PDBs have been emitted - Assert.EndsWith("debug.csx:line 1", GetStackTraceLine(ex, 0)); - } + var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFilePath("debug.csx").WithFileEncoding(Encoding.UTF8); + return VerifyStackTraceAsync(() => CSharpScript.Create("throw new System.Exception();", opts), "debug.csx:line 1"); } [Fact] - public async Task Pdb_CodeFromFile_WithoutEmitDebugInformation_WithoutFileEncoding_ResultInPdbNotEmitted() + public Task Pdb_CreateFromString_CodeFromFile_WithoutEmitDebugInformation_WithoutFileEncoding_ResultInPdbNotEmitted() { - try - { - var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath("debug.csx").WithFileEncoding(null); - var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); - } - catch (Exception ex) - { - // line information is only available when PDBs have been emitted - Assert.Equal("at Submission#0.<>d__0.MoveNext()", GetStackTraceLine(ex, 0)); - } + var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath(null).WithFileEncoding(null); + return VerifyStackTraceAsync(() => CSharpScript.Create("throw new System.Exception();", opts), "at Submission#0.<>d__0.MoveNext()"); } [Fact] - public async Task Pdb_CodeFromFile_WithoutEmitDebugInformation_WithFileEncoding_ResultInPdbNotEmitted() + public Task Pdb_CreateFromString_CodeFromFile_WithoutEmitDebugInformation_WithFileEncoding_ResultInPdbNotEmitted() { - try - { - var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath("debug.csx").WithFileEncoding(Encoding.UTF8); - var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); - } - catch (Exception ex) - { - // line information is only available when PDBs have been emitted - Assert.Equal("at Submission#0.<>d__0.MoveNext()", GetStackTraceLine(ex, 0)); - } + var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath("debug.csx").WithFileEncoding(Encoding.UTF8); + return VerifyStackTraceAsync(() => CSharpScript.Create("throw new System.Exception();", opts), "at Submission#0.<>d__0.MoveNext()"); } [Fact] - public async Task Pdb_InlineCode_WithEmitDebugInformation_WithoutFileEncoding_ResultInPdbEmitted() + public Task Pdb_CreateFromStream_CodeFromFile_WithEmitDebugInformation_ResultInPdbEmitted() { - try - { - var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFileEncoding(null); - var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); - } - catch (Exception ex) - { - // line information is only available when PDBs have been emitted - Assert.Equal("at Submission#0.<>d__0.MoveNext() in :line 1", GetStackTraceLine(ex, 0)); - } + var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFilePath("debug.csx"); + return VerifyStackTraceAsync(() => CSharpScript.Create(new MemoryStream(Encoding.UTF8.GetBytes("throw new System.Exception();")), opts), "debug.csx:line 1"); } [Fact] - public async Task Pdb_InlineCode_WithEmitDebugInformation_WithFileEncoding_ResultInPdbEmitted() + public Task Pdb_CreateFromStream_CodeFromFile_WithoutEmitDebugInformation_ResultInPdbNotEmitted() { - try - { - var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFileEncoding(Encoding.UTF8); - var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); - } - catch (Exception ex) - { - // line information is only available when PDBs have been emitted - Assert.Equal("at Submission#0.<>d__0.MoveNext() in :line 1", GetStackTraceLine(ex, 0)); - } + var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFilePath("debug.csx"); + return VerifyStackTraceAsync(() => CSharpScript.Create(new MemoryStream(Encoding.UTF8.GetBytes("throw new System.Exception();")), opts), "at Submission#0.<>d__0.MoveNext()"); } [Fact] - public async Task Pdb_InlineCode_WithoutEmitDebugInformation_WithoutFileEncoding_ResultInPdbNotEmitted() + public Task Pdb_CreateFromString_InlineCode_WithEmitDebugInformation_WithoutFileEncoding_ResultInPdbEmitted() { - try - { - var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFileEncoding(null); - var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); - } - catch (Exception ex) - { - // line information is only available when PDBs have been emitted - Assert.Equal("at Submission#0.<>d__0.MoveNext()", GetStackTraceLine(ex, 0)); - } + var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFileEncoding(null); + return VerifyStackTraceAsync(() => CSharpScript.Create("throw new System.Exception();", opts), "at Submission#0.<>d__0.MoveNext() in :line 1"); } [Fact] - public async Task Pdb_InlineCode_WithoutEmitDebugInformation_WithFileEncoding_ResultInPdbNotEmitted() + public Task Pdb_CreateFromString_InlineCode_WithEmitDebugInformation_WithFileEncoding_ResultInPdbEmitted() { - try - { - var opts = ScriptOptions.Default.WithEmitDebugInformation(false).WithFileEncoding(Encoding.UTF8); - var script = await CSharpScript.RunAsync("throw new System.Exception();", opts); - } - catch (Exception ex) - { - // line information is only available when PDBs have been emitted - Assert.Equal("at Submission#0.<>d__0.MoveNext()", GetStackTraceLine(ex, 0)); - } + var opts = ScriptOptions.Default.WithEmitDebugInformation(true).WithFileEncoding(Encoding.UTF8); + return VerifyStackTraceAsync(() => CSharpScript.Create("throw new System.Exception();", opts), "at Submission#0.<>d__0.MoveNext() in :line 1"); + } + + [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), "at Submission#0.<>d__0.MoveNext()"); + } + + [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), "at Submission#0.<>d__0.MoveNext()"); + } + + [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), "at Submission#0.<>d__0.MoveNext() in :line 1"); + } + + [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), "at Submission#0.<>d__0.MoveNext()"); } [WorkItem(12348, "https://github.com/dotnet/roslyn/issues/12348")] @@ -896,5 +868,19 @@ private string GetStackTraceLine(Exception ex, int index) return null; } + + private async Task VerifyStackTraceAsync(Func> scriptProvider, string expectedFirstLineEnding) + { + try + { + var script = scriptProvider(); + await script.RunAsync(); + } + catch (Exception ex) + { + // line information is only available when PDBs have been emitted + Assert.EndsWith(expectedFirstLineEnding, GetStackTraceLine(ex, 0)); + } + } } } From 5fbe48ba2b1ac0e5666281d9ef0cfd556c8a3e3f Mon Sep 17 00:00:00 2001 From: filipw Date: Tue, 7 Feb 2017 16:43:47 +0100 Subject: [PATCH 18/20] check parameters for null improve tests by using StackTrace API and diagnostic verifier --- src/Scripting/CSharp/CSharpScript.cs | 2 + src/Scripting/CSharpTest/ScriptTests.cs | 50 +++++++++++-------------- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/Scripting/CSharp/CSharpScript.cs b/src/Scripting/CSharp/CSharpScript.cs index 3576314095d45..1b97f0ce41b04 100644 --- a/src/Scripting/CSharp/CSharpScript.cs +++ b/src/Scripting/CSharp/CSharpScript.cs @@ -41,6 +41,7 @@ public static Script Create(string code, ScriptOptions options = null, Typ /// Stream is not readable or seekable. public static Script Create(Stream code, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null) { + if (code == null) throw new ArgumentNullException(nameof(code)); return Script.CreateInitialScript(CSharpScriptCompiler.Instance, SourceText.From(code, options?.FileEncoding), options, globalsType, assemblyLoader); } @@ -67,6 +68,7 @@ public static Script Create(string code, ScriptOptions options = null, T /// Stream is not readable or seekable. public static Script Create(Stream code, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null) { + if (code == null) throw new ArgumentNullException(nameof(code)); return Create(code, options, globalsType, assemblyLoader); } diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index 9250e9b0a6825..4c7fd19ab2d9e 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -12,6 +12,7 @@ using System.IO; using System.Globalization; using System.Text; +using System.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests { @@ -715,14 +716,16 @@ public async Task LoadedFileWithVoidReturn() [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("throw new System.Exception();", opts); + var script = await CSharpScript.RunAsync(code, opts); } catch (CompilationErrorException ex) { - Assert.EndsWith("error CS8055: Cannot emit debug information for a source text without encoding.", ex.Message); + // CS8055: Cannot emit debug information for a source text without encoding. + ex.Diagnostics.Verify(Diagnostic(ErrorCode.ERR_EncodinglessSyntaxTree, code).WithLocation(1,1)); } } @@ -730,77 +733,77 @@ public async Task Pdb_CreateFromString_CodeFromFile_WithEmitDebugInformation_Wit 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), "debug.csx:line 1"); + 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), "at Submission#0.<>d__0.MoveNext()"); + 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), "at Submission#0.<>d__0.MoveNext()"); + 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), "debug.csx:line 1"); + 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), "at Submission#0.<>d__0.MoveNext()"); + 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), "at Submission#0.<>d__0.MoveNext() in :line 1"); + 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), "at Submission#0.<>d__0.MoveNext() in :line 1"); + 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), "at Submission#0.<>d__0.MoveNext()"); + 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), "at Submission#0.<>d__0.MoveNext()"); + 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), "at Submission#0.<>d__0.MoveNext() in :line 1"); + 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), "at Submission#0.<>d__0.MoveNext()"); + return VerifyStackTraceAsync(() => CSharpScript.Create(new MemoryStream(Encoding.UTF8.GetBytes("throw new System.Exception();")), opts)); } [WorkItem(12348, "https://github.com/dotnet/roslyn/issues/12348")] @@ -856,20 +859,7 @@ public override Stream OpenRead(string resolvedPath) } } - private string GetStackTraceLine(Exception ex, int index) - { - if (ex == null || ex.StackTrace == null) return null; - - var stackTrace = ex.StackTrace?.Split(new[] { Environment.NewLine }, StringSplitOptions.None); - if (stackTrace.Length >= index) - { - return stackTrace[index].Trim(); - } - - return null; - } - - private async Task VerifyStackTraceAsync(Func> scriptProvider, string expectedFirstLineEnding) + private async Task VerifyStackTraceAsync(Func> scriptProvider, int line = 0, int column = 0, string filename = null) { try { @@ -879,7 +869,11 @@ private async Task VerifyStackTraceAsync(Func> scriptProvider, st catch (Exception ex) { // line information is only available when PDBs have been emitted - Assert.EndsWith(expectedFirstLineEnding, GetStackTraceLine(ex, 0)); + 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()); } } } From 931fdf92ecb92ceb6f7d96a2884a09a0923ba03d Mon Sep 17 00:00:00 2001 From: filipw Date: Tue, 7 Feb 2017 17:11:43 +0100 Subject: [PATCH 19/20] applied review feedback --- .../Core/Hosting/CommandLine/CommandLineRunner.cs | 4 ++-- src/Scripting/Core/Script.cs | 15 +++++++++++---- src/Scripting/Core/ScriptOptions.cs | 3 ++- src/Scripting/CoreTest/ScriptOptionsTests.cs | 11 +++++------ 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/Scripting/Core/Hosting/CommandLine/CommandLineRunner.cs b/src/Scripting/Core/Hosting/CommandLine/CommandLineRunner.cs index 6a1a5ffb3f2d9..d3bd825f0fe3f 100644 --- a/src/Scripting/Core/Hosting/CommandLine/CommandLineRunner.cs +++ b/src/Scripting/Core/Hosting/CommandLine/CommandLineRunner.cs @@ -178,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(_scriptCompiler, SourceText.From(code ?? string.Empty), options, globals.GetType(), assemblyLoaderOpt: null); + var script = Script.CreateInitialScript(_scriptCompiler, SourceText.From(code), options, globals.GetType(), assemblyLoaderOpt: null); try { return script.RunAsync(globals, cancellationToken).Result.ReturnValue; @@ -199,7 +199,7 @@ private void RunInteractiveLoop(ScriptOptions options, string initialScriptCodeO if (initialScriptCodeOpt != null) { - var script = Script.CreateInitialScript(_scriptCompiler, SourceText.From(initialScriptCodeOpt ?? string.Empty), options, globals.GetType(), assemblyLoaderOpt: null); + var script = Script.CreateInitialScript(_scriptCompiler, SourceText.From(initialScriptCodeOpt), options, globals.GetType(), assemblyLoaderOpt: null); BuildAndRun(script, globals, ref state, ref options, displayResult: false, cancellationToken: cancellationToken); } diff --git a/src/Scripting/Core/Script.cs b/src/Scripting/Core/Script.cs index cefdc65bef634..539f243b38c97 100644 --- a/src/Scripting/Core/Script.cs +++ b/src/Scripting/Core/Script.cs @@ -105,16 +105,23 @@ public Script ContinueWith(Stream code, ScriptOptions options = null) => /// /// Continues the script with given code snippet. /// - public Script ContinueWith(string code, ScriptOptions options = null) => - new Script(Compiler, Builder, SourceText.From(code ?? "", options?.FileEncoding ?? Options.FileEncoding), options ?? InheritOptions(Options), GlobalsType, this); + public Script ContinueWith(string code, ScriptOptions options = null) + { + options = options ?? InheritOptions(Options); + return new Script(Compiler, Builder, SourceText.From(code ?? "", options.FileEncoding), options, GlobalsType, this); + } /// /// Continues the script with given representing code. /// /// Stream is null. /// Stream is not readable or seekable. - public Script ContinueWith(Stream code, ScriptOptions options = null) => - new Script(Compiler, Builder, SourceText.From(code, options?.FileEncoding ?? Options.FileEncoding), options ?? InheritOptions(Options), GlobalsType, this); + public Script ContinueWith(Stream code, ScriptOptions options = null) + { + if (code == null) throw new ArgumentNullException(nameof(code)); + options = options ?? InheritOptions(Options); + return new Script(Compiler, Builder, SourceText.From(code, options.FileEncoding), options, GlobalsType, this); + } private static ScriptOptions InheritOptions(ScriptOptions previous) { diff --git a/src/Scripting/Core/ScriptOptions.cs b/src/Scripting/Core/ScriptOptions.cs index ed89fb5e52ee4..3729276359469 100644 --- a/src/Scripting/Core/ScriptOptions.cs +++ b/src/Scripting/Core/ScriptOptions.cs @@ -100,7 +100,8 @@ private static ImmutableArray GetDefaultMetadataReferences() public bool EmitDebugInformation { get; private set; } = false; /// - /// Specifies the encoding to be used when debugging scripts loaded from a file, or that will be saved to a file for debugging purposes. + /// 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 /// public Encoding FileEncoding { get; private set; } diff --git a/src/Scripting/CoreTest/ScriptOptionsTests.cs b/src/Scripting/CoreTest/ScriptOptionsTests.cs index 210921400f6d9..54f16418be1bc 100644 --- a/src/Scripting/CoreTest/ScriptOptionsTests.cs +++ b/src/Scripting/CoreTest/ScriptOptionsTests.cs @@ -151,13 +151,12 @@ public void WithImports_Errors() options.WithImports(".blah"); } - [Theory] - [InlineData(true)] - [InlineData(false)] - public void WithEmitDebugInformation_SetsEmitDebugInformation(bool emitDebugInformation) + [Fact] + public void WithEmitDebugInformation_SetsEmitDebugInformation() { - var options = ScriptOptions.Default.WithEmitDebugInformation(emitDebugInformation); - Assert.Equal(emitDebugInformation, options.EmitDebugInformation); + Assert.True(ScriptOptions.Default.WithEmitDebugInformation(true).EmitDebugInformation); + Assert.False(ScriptOptions.Default.WithEmitDebugInformation(false).EmitDebugInformation); + Assert.False(ScriptOptions.Default.EmitDebugInformation); } [Fact] From 3160a3e0aeb8a2f8330644e560e62de9f222af0c Mon Sep 17 00:00:00 2001 From: filipw Date: Tue, 7 Feb 2017 23:26:23 +0100 Subject: [PATCH 20/20] added missing null check in CSharpScript Create --- src/Scripting/CSharp/CSharpScript.cs | 4 ++++ src/Scripting/CSharpTest/ScriptTests.cs | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/Scripting/CSharp/CSharpScript.cs b/src/Scripting/CSharp/CSharpScript.cs index 1b97f0ce41b04..581e98394f375 100644 --- a/src/Scripting/CSharp/CSharpScript.cs +++ b/src/Scripting/CSharp/CSharpScript.cs @@ -24,8 +24,10 @@ public static class CSharpScript /// Type of global object. /// Custom assembly loader. /// The return type of the script + /// Code is null. public static Script Create(string code, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null) { + if (code == null) throw new ArgumentNullException(nameof(code)); return Script.CreateInitialScript(CSharpScriptCompiler.Instance, SourceText.From(code, options?.FileEncoding), options, globalsType, assemblyLoader); } @@ -52,8 +54,10 @@ public static Script Create(Stream code, ScriptOptions options = null, Typ /// The script options. /// Type of global object. /// Custom assembly loader. + /// Code is null. public static Script Create(string code, ScriptOptions options = null, Type globalsType = null, InteractiveAssemblyLoader assemblyLoader = null) { + if (code == null) throw new ArgumentNullException(nameof(code)); return Create(code, options, globalsType, assemblyLoader); } diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index 4c7fd19ab2d9e..479948b6d07e0 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -31,6 +31,12 @@ public void TestCreateScript() Assert.Equal("1 + 2", script.Code); } + [Fact] + public void TestCreateScript_CodeIsNull() + { + Assert.Throws(() => CSharpScript.Create((string)null)); + } + [Fact] public void TestCreateFromStreamScript() { @@ -38,6 +44,12 @@ public void TestCreateFromStreamScript() Assert.Equal("1 + 2", script.Code); } + [Fact] + public void TestCreateFromStreamScript_StreamIsNull() + { + Assert.Throws(() => CSharpScript.Create((Stream)null)); + } + [Fact] public async Task TestGetCompilation() {