Skip to content

Commit d791264

Browse files
authored
Merge pull request dotnet-script#211 from filipw/bugfix/203
Handle #load and #r in eval
2 parents a0cad71 + 1270569 commit d791264

File tree

10 files changed

+124
-18
lines changed

10 files changed

+124
-18
lines changed

src/Dotnet.Script.Core/Interactive/InteractiveRunner.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System;
1212
using System.Collections.Immutable;
1313
using Dotnet.Script.DependencyModel.NuGet;
14+
using Dotnet.Script.DependencyModel.Context;
1415

1516
namespace Dotnet.Script.Core
1617
{
@@ -67,14 +68,14 @@ await HandleScriptErrors(async () =>
6768
if (_scriptState == null)
6869
{
6970
var sourceText = SourceText.From(input);
70-
var context = new ScriptContext(sourceText, CurrentDirectory, Enumerable.Empty<string>(), debugMode: debugMode);
71+
var context = new ScriptContext(sourceText, CurrentDirectory, Enumerable.Empty<string>(), debugMode: debugMode, scriptMode: ScriptMode.REPL);
7172
await RunFirstScript(context);
7273
}
7374
else
7475
{
7576
if (input.StartsWith("#r ") || input.StartsWith("#load "))
7677
{
77-
var lineRuntimeDependencies = ScriptCompiler.RuntimeDependencyResolver.GetDependenciesFromCode(CurrentDirectory, input).ToArray();
78+
var lineRuntimeDependencies = ScriptCompiler.RuntimeDependencyResolver.GetDependencies(CurrentDirectory, ScriptMode.REPL, input).ToArray();
7879
var lineDependencies = lineRuntimeDependencies.SelectMany(rtd => rtd.Assemblies).Distinct();
7980

8081
var scriptMap = lineRuntimeDependencies.ToDictionary(rdt => rdt.Name, rdt => rdt.Scripts);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System.Linq;
2+
using Microsoft.CodeAnalysis.CSharp;
3+
using Microsoft.CodeAnalysis;
4+
using Microsoft.CodeAnalysis.CSharp.Syntax;
5+
6+
namespace Dotnet.Script
7+
{
8+
internal class PreprocessorLineRewriter : CSharpSyntaxRewriter
9+
{
10+
public PreprocessorLineRewriter()
11+
: base(visitIntoStructuredTrivia: true)
12+
{
13+
}
14+
15+
public override SyntaxNode VisitLoadDirectiveTrivia(LoadDirectiveTriviaSyntax node)
16+
{
17+
return HandleSkippedTrivia(base.VisitLoadDirectiveTrivia(node));
18+
}
19+
20+
public override SyntaxNode VisitReferenceDirectiveTrivia(ReferenceDirectiveTriviaSyntax node)
21+
{
22+
return HandleSkippedTrivia(base.VisitReferenceDirectiveTrivia(node));
23+
}
24+
25+
private SyntaxNode HandleSkippedTrivia(SyntaxNode node)
26+
{
27+
var skippedTrivia = node.DescendantTrivia().Where(x => x.RawKind == (int)SyntaxKind.SkippedTokensTrivia).FirstOrDefault();
28+
if (skippedTrivia != null && skippedTrivia.Token.Kind() != SyntaxKind.None)
29+
{
30+
var firstToken = skippedTrivia.GetStructure().ChildTokens().FirstOrDefault();
31+
if (firstToken != null && firstToken.Kind() == SyntaxKind.BadToken && firstToken.ToFullString().Trim() == ";")
32+
{
33+
node = node.ReplaceToken(firstToken, SyntaxFactory.Token(SyntaxKind.None));
34+
skippedTrivia = node.DescendantTrivia().Where(x => x.RawKind == (int)SyntaxKind.SkippedTokensTrivia).FirstOrDefault();
35+
}
36+
37+
node = node.ReplaceTrivia(skippedTrivia, SyntaxFactory.TriviaList(SyntaxFactory.LineFeed, skippedTrivia));
38+
39+
return node;
40+
}
41+
42+
return node;
43+
}
44+
}
45+
}

src/Dotnet.Script.Core/ScriptCompiler.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ static ScriptCompiler()
6060
// see: https://github.com/dotnet/roslyn/issues/5501
6161
protected virtual IEnumerable<string> SuppressedDiagnosticIds => new[] { "CS1701", "CS1702", "CS1705" };
6262

63+
public CSharpParseOptions ParseOptions { get; } = new CSharpParseOptions(LanguageVersion.Latest, kind: SourceCodeKind.Script);
64+
6365
public RuntimeDependencyResolver RuntimeDependencyResolver { get; }
6466

6567
public ScriptLogger Logger { get; }
@@ -94,10 +96,7 @@ public virtual ScriptCompilationContext<TReturn> CreateCompilationContext<TRetur
9496
var platformIdentifier = RuntimeHelper.GetPlatformIdentifier();
9597
Logger.Verbose($"Current runtime is '{platformIdentifier}'.");
9698

97-
var runtimeDependencies = context.FilePath != null
98-
? RuntimeDependencyResolver.GetDependencies(context.WorkingDirectory).ToArray()
99-
: RuntimeDependencyResolver.GetDependenciesFromCode(context.WorkingDirectory, context.Code.ToString()).ToArray();
100-
99+
var runtimeDependencies = RuntimeDependencyResolver.GetDependencies(context.WorkingDirectory, context.ScriptMode, context.Code.ToString()).ToArray();
101100
var opts = CreateScriptOptions(context, runtimeDependencies.ToList());
102101

103102
var runtimeId = RuntimeHelper.GetRuntimeIdentifier();
@@ -138,8 +137,22 @@ public virtual ScriptCompilationContext<TReturn> CreateCompilationContext<TRetur
138137
AssemblyLoadContext.Default.Resolving +=
139138
(assemblyLoadContext, assemblyName) => MapUnresolvedAssemblyToRuntimeLibrary(dependencyMap, assemblyLoadContext, assemblyName);
140139

140+
// when processing raw code, make sure we inject new lines after preprocessor directives
141+
string code;
142+
if (context.FilePath == null)
143+
{
144+
var syntaxTree = CSharpSyntaxTree.ParseText(context.Code, ParseOptions);
145+
var syntaxRewriter = new PreprocessorLineRewriter();
146+
var newSyntaxTree = syntaxRewriter.Visit(syntaxTree.GetRoot());
147+
code = newSyntaxTree.ToFullString();
148+
}
149+
else
150+
{
151+
code = context.Code.ToString();
152+
}
153+
141154
var loader = new InteractiveAssemblyLoader();
142-
var script = CSharpScript.Create<TReturn>(context.Code.ToString(), opts, typeof(THost), loader);
155+
var script = CSharpScript.Create<TReturn>(code, opts, typeof(THost), loader);
143156
var orderedDiagnostics = script.GetDiagnostics(SuppressedDiagnosticIds);
144157

145158
if (orderedDiagnostics.Any(d => d.Severity == DiagnosticSeverity.Error))

src/Dotnet.Script.Core/ScriptContext.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
using System.Collections.Generic;
22
using System.Collections.ObjectModel;
33
using System.Linq;
4+
using Dotnet.Script.DependencyModel.Context;
45
using Microsoft.CodeAnalysis.Text;
56

67
namespace Dotnet.Script.Core
78
{
89
public class ScriptContext
910
{
10-
public ScriptContext(SourceText code, string workingDirectory, IEnumerable<string> args, string filePath = null, bool debugMode = false)
11+
public ScriptContext(SourceText code, string workingDirectory, IEnumerable<string> args, string filePath = null, bool debugMode = false, ScriptMode scriptMode = ScriptMode.Script)
1112
{
1213
Code = code;
1314
WorkingDirectory = workingDirectory;
1415
Args = new ReadOnlyCollection<string>(args.ToArray());
1516
FilePath = filePath;
1617
DebugMode = debugMode;
18+
ScriptMode = filePath != null ? ScriptMode.Script : scriptMode;
1719
}
1820

1921
public SourceText Code { get; }
@@ -25,5 +27,7 @@ public ScriptContext(SourceText code, string workingDirectory, IEnumerable<strin
2527
public string FilePath { get; }
2628

2729
public bool DebugMode { get; }
30+
31+
public ScriptMode ScriptMode { get; }
2832
}
2933
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Dotnet.Script.DependencyModel.Context
2+
{
3+
public enum ScriptMode
4+
{
5+
Script,
6+
Eval,
7+
REPL
8+
}
9+
}

src/Dotnet.Script.DependencyModel/ProjectSystem/ScriptProjectProvider.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.IO;
1+
using System;
2+
using System.IO;
23
using System.Linq;
34
using Dotnet.Script.DependencyModel.Environment;
45
using Dotnet.Script.DependencyModel.Logging;
@@ -25,7 +26,7 @@ public string CreateProjectForRepl(string code, string targetDirectory, string d
2526
{
2627
var parseresult = _scriptParser.ParseFromCode(code);
2728

28-
targetDirectory = Path.Combine(targetDirectory, "REPL");
29+
targetDirectory = Path.Combine(targetDirectory, "interactive");
2930
var pathToProjectFile = GetPathToProjectFile(targetDirectory);
3031
var projectFile = new ProjectFile();
3132

src/Dotnet.Script.DependencyModel/Runtime/RuntimeDependencyResolver.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,12 @@ private static IRestorer[] CreateRestorers(LogFactory logFactory)
4545
return new IRestorer[] { new DotnetRestorer(commandRunner, logFactory)};
4646
}
4747

48-
public IEnumerable<RuntimeDependency> GetDependenciesFromCode(string targetDirectory, string code)
48+
public IEnumerable<RuntimeDependency> GetDependencies(string targetDirectory, ScriptMode scriptMode, string code = null)
4949
{
50-
var pathToProjectFile = _scriptProjectProvider.CreateProjectForRepl(code, targetDirectory, "netcoreapp2.0");
51-
return GetDependenciesInternal(pathToProjectFile);
52-
}
50+
var pathToProjectFile = scriptMode == ScriptMode.Script
51+
? _scriptProjectProvider.CreateProject(targetDirectory, "netcoreapp2.0", true)
52+
: _scriptProjectProvider.CreateProjectForRepl(code, Path.Combine(targetDirectory, scriptMode.ToString()), "netcoreapp2.0");
5353

54-
public IEnumerable<RuntimeDependency> GetDependencies(string targetDirectory)
55-
{
56-
var pathToProjectFile = _scriptProjectProvider.CreateProject(targetDirectory, "netcoreapp2.0", true);
5754
return GetDependenciesInternal(pathToProjectFile);
5855
}
5956

src/Dotnet.Script.Tests/ScriptExecutionTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,42 @@ public void ShouldHandleCSharp72()
139139
Assert.Contains("hi", result.output);
140140
}
141141

142+
[Fact]
143+
public void ShouldEvaluateCode()
144+
{
145+
var code = "Console.WriteLine(12345);";
146+
var result = ExecuteCode(code);
147+
Assert.Contains("12345", result.output);
148+
}
149+
150+
[Fact]
151+
public void ShouldSupportInlineNugetReferencesinEvaluatedCode()
152+
{
153+
var code = @"#r \""nuget: AutoMapper, 6.1.1\"" using AutoMapper; Console.WriteLine(typeof(MapperConfiguration));";
154+
var result = ExecuteCode(code);
155+
Assert.Contains("AutoMapper.MapperConfiguration", result.output);
156+
}
157+
158+
[Fact]
159+
public void ShouldSupportInlineNugetReferencesWithTrailingSemicoloninEvaluatedCode()
160+
{
161+
var code = @"#r \""nuget: AutoMapper, 6.1.1\""; using AutoMapper; Console.WriteLine(typeof(MapperConfiguration));";
162+
var result = ExecuteCode(code);
163+
Assert.Contains("AutoMapper.MapperConfiguration", result.output);
164+
}
165+
142166
private static (string output, int exitCode) Execute(string fixture, params string[] arguments)
143167
{
144168
var result = ProcessHelper.RunAndCaptureOutput("dotnet", GetDotnetScriptArguments(Path.Combine("..", "..", "..", "TestFixtures", fixture), arguments));
145169
return result;
146170
}
147171

172+
private static (string output, int exitCode) ExecuteCode(string code)
173+
{
174+
var result = ProcessHelper.RunAndCaptureOutput("dotnet", GetDotnetScriptArguments($"eval", new[] { $"\"{code}\"" }));
175+
return result;
176+
}
177+
148178
/// <summary>
149179
/// Use this method if you need to debug
150180
/// </summary>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
parallelizeTestCollections false
3+
}

src/Dotnet.Script/Program.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
using Dotnet.Script.DependencyModel.Logging;
1111
using Dotnet.Script.DependencyModel.Runtime;
1212
using Microsoft.CodeAnalysis.Scripting;
13+
using Microsoft.CodeAnalysis.CSharp;
14+
using Microsoft.CodeAnalysis;
15+
using Dotnet.Script.DependencyModel.Context;
1316

1417
namespace Dotnet.Script
1518
{
@@ -164,7 +167,7 @@ private static async Task RunInteractive(bool debugMode)
164167
private static Task<int> RunCode(string code, bool debugMode, IEnumerable<string> args, string currentWorkingDirectory)
165168
{
166169
var sourceText = SourceText.From(code);
167-
var context = new ScriptContext(sourceText, currentWorkingDirectory ?? Directory.GetCurrentDirectory(), args, null, debugMode);
170+
var context = new ScriptContext(sourceText, currentWorkingDirectory ?? Directory.GetCurrentDirectory(), args, null, debugMode, ScriptMode.Eval);
168171
return Run(debugMode, context);
169172
}
170173

0 commit comments

Comments
 (0)