Skip to content

Commit 2fed7a4

Browse files
committed
pr feedback
adding scriptEmitter
1 parent 245876a commit 2fed7a4

File tree

7 files changed

+139
-39
lines changed

7 files changed

+139
-39
lines changed

src/Dotnet.Script.Core/ScriptCompiler.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Linq;
55
using System.Reflection;
66
using System.Runtime.InteropServices;
7+
using System.Text;
78
using System.Threading.Tasks;
89
using Dotnet.Script.Core.Internal;
910
using Dotnet.Script.DependencyModel.Context;
@@ -52,7 +53,7 @@ static ScriptCompiler()
5253
};
5354

5455
// see: https://github.com/dotnet/roslyn/issues/5501
55-
protected virtual IEnumerable<string> SuppressedDiagnosticIds => new[] { "CS1701", "CS1702", "CS1705" };
56+
protected virtual IEnumerable<string> SuppressedDiagnosticIds => new[] { "CS1701", "CS1702", "CS1705" };
5657

5758
public CSharpParseOptions ParseOptions { get; } = new CSharpParseOptions(LanguageVersion.Latest, kind: SourceCodeKind.Script);
5859

@@ -106,7 +107,9 @@ public virtual ScriptCompilationContext<TReturn> CreateCompilationContext<TRetur
106107
Logger.Verbose($"Current runtime is '{_scriptEnvironment.PlatformIdentifier}'.");
107108
RuntimeDependency[] runtimeDependencies = GetRuntimeDependencies(context);
108109

109-
var scriptOptions = CreateScriptOptions(context, runtimeDependencies.ToList());
110+
var encoding = context.Code.Encoding ?? Encoding.UTF8; // encoding is required when emitting debug information
111+
var scriptOptions = CreateScriptOptions(context, runtimeDependencies.ToList())
112+
.WithFileEncoding(encoding);
110113

111114
var loadedAssembliesMap = CreateLoadedAssembliesMap();
112115

@@ -209,7 +212,7 @@ private void SetOptimizationLevel<TReturn>(ScriptContext context, Script<TReturn
209212
private string GetScriptCode(ScriptContext context)
210213
{
211214
string code;
212-
215+
213216
// when processing raw code, make sure we inject new lines after preprocessor directives
214217
if (context.FilePath == null)
215218
{
@@ -259,7 +262,7 @@ private Assembly MapUnresolvedAssemblyToRuntimeLibrary(IDictionary<string, Runti
259262
{
260263
if (runtimeAssembly.Name.Version > assemblyName.Version)
261264
{
262-
loadedAssemblyMap.TryGetValue(assemblyName.Name, out var loadedAssembly);
265+
loadedAssemblyMap.TryGetValue(assemblyName.Name, out var loadedAssembly);
263266
if(loadedAssembly != null)
264267
{
265268
Logger.Log($"Redirecting {assemblyName} to already loaded {loadedAssembly.GetName().Name}");
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Microsoft.CodeAnalysis;
2+
using System.Collections.Generic;
3+
using System.Collections.Immutable;
4+
using System.IO;
5+
using System.Linq;
6+
7+
namespace Dotnet.Script.Core
8+
{
9+
public class ScriptEmitResult
10+
{
11+
private ScriptEmitResult() { }
12+
13+
public ScriptEmitResult(MemoryStream peStream, MemoryStream pdbStream, IEnumerable<MetadataReference> directiveReferences)
14+
{
15+
PeStream = peStream;
16+
PdbStream = pdbStream;
17+
DirectiveReferences = directiveReferences.ToImmutableArray();
18+
}
19+
20+
public MemoryStream PeStream { get; }
21+
public MemoryStream PdbStream { get; }
22+
public ImmutableArray<Diagnostic> Diagnostics { get; private set; } = ImmutableArray.Create<Diagnostic>();
23+
public ImmutableArray<MetadataReference> DirectiveReferences { get; } = ImmutableArray.Create<MetadataReference>();
24+
public bool IsErrored => Diagnostics.Any();
25+
26+
public static ScriptEmitResult Error(IEnumerable<Diagnostic> diagnostics)
27+
{
28+
var result = new ScriptEmitResult
29+
{
30+
Diagnostics = diagnostics.ToImmutableArray()
31+
};
32+
return result;
33+
}
34+
}
35+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using Microsoft.CodeAnalysis.Emit;
2+
using Microsoft.CodeAnalysis.Scripting;
3+
using Microsoft.CodeAnalysis.Scripting.Hosting;
4+
using System.IO;
5+
6+
namespace Dotnet.Script.Core
7+
{
8+
public class ScriptEmitter
9+
{
10+
private readonly ScriptConsole _scriptConsole;
11+
private readonly ScriptCompiler _scriptCompiler;
12+
13+
public ScriptEmitter(ScriptConsole scriptConsole, ScriptCompiler scriptCompiler)
14+
{
15+
_scriptConsole = scriptConsole;
16+
_scriptCompiler = scriptCompiler;
17+
}
18+
19+
public virtual ScriptEmitResult Emit<TReturn>(ScriptContext context, string assemblyName = null)
20+
{
21+
try
22+
{
23+
var compilationContext = _scriptCompiler.CreateCompilationContext<TReturn, CommandLineScriptGlobals>(context);
24+
25+
var compilation = compilationContext.Script.GetCompilation();
26+
if (!string.IsNullOrEmpty(assemblyName))
27+
{
28+
var compilationOptions = compilationContext.Script.GetCompilation().Options
29+
.WithScriptClassName(assemblyName);
30+
compilation = compilationContext.Script.GetCompilation()
31+
.WithOptions(compilationOptions)
32+
.WithAssemblyName(assemblyName);
33+
}
34+
35+
36+
var peStream = new MemoryStream();
37+
var pdbStream = new MemoryStream();
38+
var result = compilation.Emit(peStream, pdbStream: pdbStream, options: new EmitOptions().
39+
WithDebugInformationFormat(DebugInformationFormat.PortablePdb));
40+
41+
if (result.Success)
42+
{
43+
return new ScriptEmitResult(peStream, pdbStream, compilation.DirectiveReferences);
44+
}
45+
46+
return ScriptEmitResult.Error(result.Diagnostics);
47+
}
48+
catch (CompilationErrorException e)
49+
{
50+
foreach (var diagnostic in e.Diagnostics)
51+
{
52+
_scriptConsole.WritePrettyError(diagnostic.ToString());
53+
}
54+
55+
throw;
56+
}
57+
}
58+
}
59+
}

src/Dotnet.Script.Core/ScriptPublisher.cs

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using Dotnet.Script.DependencyModel.ProjectSystem;
55
using Microsoft.CodeAnalysis;
66
using Microsoft.CodeAnalysis.Scripting;
7-
using Microsoft.CodeAnalysis.Scripting.Hosting;
87
using System;
98
using System.IO;
109
using System.Reflection;
@@ -14,26 +13,26 @@ namespace Dotnet.Script.Core
1413
public class ScriptPublisher
1514
{
1615
const string AssemblyName = "scriptAssembly";
16+
const string ScriptingVersion = "2.8.2";
1717

1818
private readonly ScriptProjectProvider _scriptProjectProvider;
19-
private readonly ScriptCompiler _scriptCompiler;
19+
private readonly ScriptEmitter _scriptEmitter;
2020
private readonly ScriptConsole _scriptConsole;
2121
private readonly ScriptEnvironment _scriptEnvironment;
2222

23-
public ScriptPublisher(ScriptProjectProvider scriptProjectProvider, ScriptCompiler scriptCompiler,
24-
ScriptConsole scriptConsole)
23+
public ScriptPublisher(ScriptProjectProvider scriptProjectProvider, ScriptEmitter scriptEmitter, ScriptConsole scriptConsole)
2524
{
2625
_scriptProjectProvider = scriptProjectProvider ?? throw new ArgumentNullException(nameof(scriptProjectProvider));
27-
_scriptCompiler = scriptCompiler ?? throw new ArgumentNullException(nameof(scriptCompiler));
26+
_scriptEmitter = scriptEmitter ?? throw new ArgumentNullException(nameof(scriptEmitter));
2827
_scriptConsole = scriptConsole ?? throw new ArgumentNullException(nameof(scriptConsole));
2928
_scriptEnvironment = ScriptEnvironment.Default;
3029
}
3130

32-
public ScriptPublisher(LogFactory logFactory, ScriptCompiler scriptCompiler)
31+
public ScriptPublisher(LogFactory logFactory, ScriptEmitter scriptEmitter)
3332
: this
3433
(
3534
new ScriptProjectProvider(logFactory),
36-
scriptCompiler,
35+
scriptEmitter,
3736
ScriptConsole.Default
3837
)
3938
{
@@ -47,8 +46,7 @@ public void CreateExecutable(ScriptContext context, LogFactory logFactory)
4746
var scriptAssemblyPath = CreateScriptAssembly(context, tempProjectDirecory);
4847

4948
var projectFile = new ProjectFile(File.ReadAllText(tempProjectPath));
50-
// todo: grab version in a better way?
51-
projectFile.AddPackageReference(new PackageReference("Microsoft.CodeAnalysis.Scripting", "2.8.2", PackageOrigin.ReferenceDirective));
49+
projectFile.AddPackageReference(new PackageReference("Microsoft.CodeAnalysis.Scripting", ScriptingVersion, PackageOrigin.ReferenceDirective));
5250
projectFile.AddAssemblyReference(scriptAssemblyPath);
5351
projectFile.Save(tempProjectPath);
5452

@@ -66,27 +64,37 @@ private string CreateScriptAssembly(ScriptContext context, string tempProjectDir
6664
{
6765
try
6866
{
69-
var compilationContext = _scriptCompiler.CreateCompilationContext<int, CommandLineScriptGlobals>(context);
70-
var scriptOptions = compilationContext.Script.GetCompilation().Options
71-
.WithScriptClassName(AssemblyName);
72-
var scriptCompilation = compilationContext.Script.GetCompilation()
73-
.WithOptions(scriptOptions)
74-
.WithAssemblyName(AssemblyName);
75-
76-
foreach (var reference in scriptCompilation.DirectiveReferences)
67+
var emitResult = _scriptEmitter.Emit<int>(context, AssemblyName);
68+
if (emitResult.IsErrored)
7769
{
78-
var refInfo = new FileInfo(reference.Display);
79-
File.Copy(refInfo.FullName, $"{tempProjectDirecory}/{refInfo.Name}", true);
70+
throw new CompilationErrorException("One or more errors occurred when emitting the assembly", emitResult.Diagnostics);
8071
}
8172

8273
var assemblyPath = $"{tempProjectDirecory}/{AssemblyName}.dll";
83-
var emitResult = scriptCompilation.Emit(assemblyPath);
84-
if (!emitResult.Success) throw new Exception("Failed while emitting the generated script assembly");
74+
using (var peFileStream = new FileStream(assemblyPath, FileMode.Create))
75+
using (emitResult.PeStream)
76+
{
77+
emitResult.PeStream.WriteTo(peFileStream);
78+
}
79+
80+
using (var pdbFileStream = new FileStream($"{tempProjectDirecory}/{AssemblyName}.pdb", FileMode.Create))
81+
using (emitResult.PdbStream)
82+
{
83+
emitResult.PdbStream.WriteTo(pdbFileStream);
84+
}
85+
86+
foreach (var reference in emitResult.DirectiveReferences)
87+
{
88+
if (reference.Display.EndsWith(".NuGet.dll")) continue;
89+
var refInfo = new FileInfo(reference.Display);
90+
File.Copy(refInfo.FullName, $"{tempProjectDirecory}/{refInfo.Name}", true);
91+
}
8592

8693
return assemblyPath;
8794
}
8895
catch (CompilationErrorException ex)
8996
{
97+
_scriptConsole.WritePrettyError(ex.Message);
9098
foreach (var diagnostic in ex.Diagnostics)
9199
{
92100
_scriptConsole.WritePrettyError(diagnostic.ToString());

src/Dotnet.Script.Core/Templates/program.publish.template

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
42
using Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;
53
using Microsoft.CodeAnalysis.Scripting.Hosting;
64
using System.Threading.Tasks;
@@ -10,7 +8,7 @@ namespace dotnetPublishCode
108
{
119
class Program
1210
{
13-
static void Main(string[] args)
11+
static async Task Main(string[] args)
1412
{
1513
try
1614
{
@@ -21,21 +19,16 @@ namespace dotnetPublishCode
2119
var factoryMethod = typeof(scriptAssembly).GetMethod("<Factory>");
2220
if (factoryMethod == null) throw new Exception("couldn't find factory method to initiate script");
2321

24-
// todo: not sure what the second parameter is, using 'null' for now
2522
var invokeTask = factoryMethod.Invoke(null, new object[] { new object[] { globals, null } }) as Task<int>;
26-
var invokeResult = invokeTask.Result;
23+
var invokeResult = await invokeTask;
2724
if (invokeResult != 0) WritePrettyError($"Error result: '{invokeResult}'");
2825
}
29-
// todo: not getting the full script stack trace
30-
catch (AggregateException ex)
26+
catch (Exception ex)
3127
{
32-
foreach (var exception in ex.InnerExceptions ?? Enumerable.Empty<Exception>())
28+
if (ex is AggregateException aggregateEx)
3329
{
34-
WritePrettyError(exception.ToString());
30+
ex = aggregateEx.Flatten().InnerException;
3531
}
36-
}
37-
catch (Exception ex)
38-
{
3932
WritePrettyError(ex.ToString());
4033
}
4134
}

src/Dotnet.Script.DependencyModel/ProjectSystem/csproj.template

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<OutputType>Exe</OutputType>
4-
<TargetFramework>netcoreapp2.0</TargetFramework>
4+
<TargetFramework>netcoreapp2.0</TargetFramework>
5+
<LangVersion>latest</LangVersion>
56
</PropertyGroup>
67
<ItemGroup>
78
</ItemGroup>

src/Dotnet.Script/Program.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ private static int Wain(string[] args)
151151
var publishDirectory = publishDirectoryOption.Value() ?? $"{Path.GetDirectoryName(fileNameArgument.Value)}/publish";
152152
var logFactory = GetLogFactory();
153153
var compiler = GetScriptCompiler(publishDebugMode.HasValue());
154-
var publisher = new ScriptPublisher(logFactory, compiler);
154+
var scriptEmmiter = new ScriptEmitter(ScriptConsole.Default, compiler);
155+
var publisher = new ScriptPublisher(logFactory, scriptEmmiter);
155156
var code = SourceText.From(File.ReadAllText(fileNameArgument.Value));
156157
var context = new ScriptContext(code, publishDirectory, Enumerable.Empty<string>(), fileNameArgument.Value, optimizationLevel);
157158

0 commit comments

Comments
 (0)