Skip to content

Commit 5f5f2d4

Browse files
Merge pull request #353 from kindermannhubert/syntaxInterceptionForRefStructHandling
Syntax interception for ref struct handling + formatting of (ReadOnly)Span<T>/(ReadOnly)Memory<T>
2 parents af51164 + 4d80331 commit 5f5f2d4

18 files changed

+407
-113
lines changed

CSharpRepl.Services/CSharpRepl.Services.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@
5454
</AssemblyAttribute>
5555
</ItemGroup>
5656

57+
58+
<ItemGroup>
59+
<EmbeddedResource Include="RuntimeHelper.cs" />
60+
</ItemGroup>
61+
5762
<ItemGroup>
5863
<None Update="runtime.json">
5964
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

CSharpRepl.Services/Disassembly/Disassembler.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ internal class Disassembler
2727
{
2828
private readonly AssemblyReferenceService referenceService;
2929
private readonly (string name, CompileDelegate compile)[] compilers;
30+
private readonly CSharpParseOptions parseOptions;
3031
private readonly CSharpCompilationOptions compilationOptions;
3132

32-
public Disassembler(CSharpCompilationOptions compilationOptions, AssemblyReferenceService referenceService, ScriptRunner scriptRunner)
33+
public Disassembler(CSharpParseOptions parseOptions, CSharpCompilationOptions compilationOptions, AssemblyReferenceService referenceService, ScriptRunner scriptRunner)
3334
{
35+
this.parseOptions = parseOptions.WithKind(SourceCodeKind.Regular);
3436
this.compilationOptions = compilationOptions;
3537
this.referenceService = referenceService;
3638

@@ -45,7 +47,7 @@ public Disassembler(CSharpCompilationOptions compilationOptions, AssemblyReferen
4547
compile: (code, optimizationLevel) => Compile(code, optimizationLevel, OutputKind.DynamicallyLinkedLibrary)),
4648
// Compiling as a script will work for most other cases, but it's quite verbose so we use it as a last resort.
4749
(name: "Scripting session (will be overly verbose)",
48-
compile: (code, optimizationLevel) => scriptRunner.CompileTransient(code, optimizationLevel))
50+
compile: scriptRunner.CompileTransient)
4951
];
5052
}
5153

@@ -151,7 +153,7 @@ static PlainTextOutput DisassembleAll(PEFile file, PlainTextOutput ilCodeOutput)
151153

152154
private Compilation Compile(string code, OptimizationLevel optimizationLevel, OutputKind outputKind)
153155
{
154-
var ast = CSharpSyntaxTree.ParseText(code, new CSharpParseOptions(LanguageVersion.Latest));
156+
var ast = CSharpSyntaxTree.ParseText(code, parseOptions);
155157
var compilation = CSharpCompilation.Create("CompilationForDisassembly",
156158
[ast],
157159
referenceService.LoadedReferenceAssemblies,

CSharpRepl.Services/Roslyn/Formatting/CustomObjectFormatters/IEnumerableFormatter.cs

Lines changed: 112 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -22,45 +22,46 @@ public override StyledString FormatToText(IEnumerable value, Level level, Format
2222
var sb = new StyledStringBuilder();
2323

2424
//header
25-
AppendHeader(sb, value, formatter);
26-
27-
//items
28-
sb.Append(" { ");
29-
var enumerator = value.GetEnumerator();
30-
try
25+
if (AppendHeader(sb, value, formatter))
3126
{
32-
var maxParagraphLength = LengthLimiting.GetMaxParagraphLength(level, formatter.ConsoleProfile);
33-
bool any = false;
34-
while (enumerator.MoveNext())
27+
//items
28+
sb.Append(" { ");
29+
var enumerator = value.GetEnumerator();
30+
try
3531
{
36-
if (any)
32+
var maxParagraphLength = LengthLimiting.GetMaxParagraphLength(level, formatter.ConsoleProfile);
33+
bool any = false;
34+
while (enumerator.MoveNext())
3735
{
38-
sb.Append(", ");
39-
40-
if (maxParagraphLength > sb.Length &&
41-
sb.Length > formatter.ConsoleProfile.Width / 2) //just heuristic
36+
if (any)
4237
{
43-
sb.Append(", ...");
44-
break;
38+
sb.Append(", ");
39+
40+
if (maxParagraphLength > sb.Length &&
41+
sb.Length > formatter.ConsoleProfile.Width / 2) //just heuristic
42+
{
43+
sb.Append(", ...");
44+
break;
45+
}
4546
}
47+
var formattedItem = formatter.FormatObjectToText(enumerator.Current, level.Increment());
48+
sb.Append(formattedItem);
49+
any = true;
4650
}
47-
var formattedItem = formatter.FormatObjectToText(enumerator.Current, level.Increment());
48-
sb.Append(formattedItem);
49-
any = true;
5051
}
51-
}
52-
catch (Exception ex)
53-
{
54-
sb.Append(formatter.GetValueRetrievalExceptionText(ex, level.Increment()));
55-
}
56-
finally
57-
{
58-
if (enumerator is IDisposable disposable)
52+
catch (Exception ex)
53+
{
54+
sb.Append(formatter.GetValueRetrievalExceptionText(ex, level.Increment()));
55+
}
56+
finally
5957
{
60-
disposable.Dispose();
58+
if (enumerator is IDisposable disposable)
59+
{
60+
disposable.Dispose();
61+
}
6162
}
63+
sb.Append(" }");
6264
}
63-
sb.Append(" }");
6465

6566
return sb.ToStyledString();
6667
}
@@ -73,79 +74,110 @@ public override FormattedObjectRenderable FormatToRenderable(IEnumerable value,
7374
}
7475

7576
var sb = new StyledStringBuilder();
76-
77-
AppendHeader(sb, value, formatter);
78-
var header = sb.ToStyledString().ToParagraph();
79-
80-
var table = new Table().AddColumns("Name", "Value", "Type");
81-
82-
var enumerator = value.GetEnumerator();
83-
try
77+
if (AppendHeader(sb, value, formatter))
8478
{
85-
var maxItems = LengthLimiting.GetTableMaxItems(level, formatter.ConsoleProfile);
86-
int counter = 0;
87-
while (enumerator.MoveNext())
79+
var header = sb.ToStyledString().ToParagraph();
80+
var table = new Table().AddColumns("Name", "Value", "Type");
81+
82+
var enumerator = value.GetEnumerator();
83+
try
8884
{
89-
if (counter > maxItems)
85+
var maxItems = LengthLimiting.GetTableMaxItems(level, formatter.ConsoleProfile);
86+
int counter = 0;
87+
while (enumerator.MoveNext())
9088
{
91-
table.AddRow("...", "...", "...");
92-
break;
93-
}
89+
if (counter > maxItems)
90+
{
91+
table.AddRow("...", "...", "...");
92+
break;
93+
}
9494

95-
sb.Clear();
96-
sb.Append('[').Append(formatter.FormatObjectToText(counter, Level.FirstSimple)).Append(']');
95+
sb.Clear();
96+
sb.Append('[').Append(formatter.FormatObjectToText(counter, Level.FirstSimple)).Append(']');
9797

98-
var name = sb.ToStyledString();
98+
var name = sb.ToStyledString();
9999

100-
var itemValue = formatter.FormatObjectToRenderable(enumerator.Current, level.Increment());
100+
var itemValue = formatter.FormatObjectToRenderable(enumerator.Current, level.Increment());
101101

102-
var itemType =
103-
enumerator.Current is null ?
104-
new Paragraph("") :
105-
formatter.FormatObjectToText(enumerator.Current.GetType(), level.Increment()).ToParagraph();
102+
var itemType =
103+
enumerator.Current is null ?
104+
new Paragraph("") :
105+
formatter.FormatObjectToText(enumerator.Current.GetType(), level.Increment()).ToParagraph();
106106

107-
table.AddRow(name.ToParagraph(), itemValue, itemType);
107+
table.AddRow(name.ToParagraph(), itemValue, itemType);
108108

109-
counter++;
110-
}
109+
counter++;
110+
}
111111

112-
if (counter == 0)
112+
if (counter == 0)
113+
{
114+
return new FormattedObjectRenderable(header, renderOnNewLine: false);
115+
}
116+
}
117+
catch (Exception ex)
118+
{
119+
table.AddRow(new Paragraph(""), formatter.GetValueRetrievalExceptionText(ex, level.Increment()).ToParagraph(), new Paragraph(""));
120+
}
121+
finally
113122
{
114-
return new FormattedObjectRenderable(header, renderOnNewLine: false);
123+
if (enumerator is IDisposable disposable)
124+
{
125+
disposable.Dispose();
126+
}
115127
}
128+
129+
return new FormattedObjectRenderable(
130+
new RenderableSequence(header, table, separateByLineBreak: true),
131+
renderOnNewLine: false);
116132
}
117-
catch (Exception ex)
133+
else
118134
{
119-
table.AddRow(new Paragraph(""), formatter.GetValueRetrievalExceptionText(ex, level.Increment()).ToParagraph(), new Paragraph(""));
135+
return new FormattedObjectRenderable(sb.ToStyledString().ToParagraph(), renderOnNewLine: false);
120136
}
121-
finally
137+
}
138+
139+
private static bool AppendHeader(StyledStringBuilder sb, IEnumerable value, Formatter formatter)
140+
{
141+
var type = value.GetType();
142+
143+
if (type.FullName?.EndsWith(typeof(__CSharpRepl_RuntimeHelper.CharSpanOutput).FullName!) == true)
122144
{
123-
if (enumerator is IDisposable disposable)
145+
if (type.GetField(nameof(__CSharpRepl_RuntimeHelper.CharSpanOutput.Text))?.GetValue(value) is string text &&
146+
type.GetField(nameof(__CSharpRepl_RuntimeHelper.CharSpanOutput.OriginalType))?.GetValue(value) is Type originalType)
124147
{
125-
disposable.Dispose();
148+
AppendTypeName(originalType, isArray: false);
149+
sb.Append(Environment.NewLine);
150+
sb.Append(formatter.FormatObjectToText(text, Level.FirstDetailed));
151+
return false;
152+
}
153+
}
154+
else if (type.FullName?.EndsWith(typeof(__CSharpRepl_RuntimeHelper.SpanOutput).FullName!) == true)
155+
{
156+
if (type.GetField(nameof(__CSharpRepl_RuntimeHelper.SpanOutput.OriginalType))?.GetValue(value) is Type originalType)
157+
{
158+
type = originalType;
126159
}
127160
}
128161

129-
return new FormattedObjectRenderable(
130-
new RenderableSequence(header, table, separateByLineBreak: true),
131-
renderOnNewLine: false);
132-
}
133-
134-
private static void AppendHeader(StyledStringBuilder sb, IEnumerable value, Formatter formatter)
135-
{
136162
var isArray = value is Array;
137-
sb.Append(
138-
formatter.FormatTypeName(
139-
isArray ? (value.GetType().GetElementType() ?? value.GetType()) : value.GetType(),
140-
showNamespaces: false,
141-
useLanguageKeywords: true,
142-
hideSystemNamespace: true));
143-
144-
if (TryGetCount(value, formatter, out var count))
163+
AppendTypeName(type, isArray);
164+
return true;
165+
166+
void AppendTypeName(Type type, bool isArray)
145167
{
146-
sb.Append(isArray ? '[' : '(');
147-
sb.Append(count);
148-
sb.Append(isArray ? ']' : ')');
168+
sb.Append(
169+
formatter.FormatTypeName(
170+
isArray ? (type.GetElementType() ?? type) : type,
171+
showNamespaces: false,
172+
useLanguageKeywords: true,
173+
hideSystemNamespace: true));
174+
175+
if (TryGetCount(value, formatter, out var count))
176+
{
177+
sb.Append(isArray ? '[' : '(');
178+
sb.Append(count);
179+
sb.Append(isArray ? ']' : ')');
180+
}
149181
}
150182
}
151183

CSharpRepl.Services/Roslyn/Formatting/FormattedObject.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using Spectre.Console.Rendering;
43

54
namespace CSharpRepl.Services.Roslyn.Formatting;

CSharpRepl.Services/Roslyn/References/AssemblyReferenceService.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,16 @@ internal sealed class AssemblyReferenceService
3434
private readonly HashSet<string> implementationAssemblyPaths;
3535
private readonly HashSet<string> sharedFrameworkImplementationAssemblyPaths;
3636
private readonly HashSet<UsingDirectiveSyntax> usings;
37+
private readonly CSharpParseOptions parseOptions;
3738

3839
public IReadOnlySet<string> ImplementationAssemblyPaths => implementationAssemblyPaths;
3940
public IReadOnlySet<MetadataReference> LoadedImplementationAssemblies => loadedImplementationAssemblies;
4041
public IReadOnlySet<MetadataReference> LoadedReferenceAssemblies => loadedReferenceAssemblies;
4142
public IReadOnlyCollection<UsingDirectiveSyntax> Usings => usings;
4243

43-
public AssemblyReferenceService(Configuration config, ITraceLogger logger)
44+
public AssemblyReferenceService(Configuration config, CSharpParseOptions parseOptions, ITraceLogger logger)
4445
{
46+
this.parseOptions = parseOptions;
4547
this.dotnetInstallationLocator = new DotNetInstallationLocator(logger);
4648
this.referenceAssemblyPaths = [];
4749
this.implementationAssemblyPaths = [];
@@ -201,7 +203,7 @@ public void LoadSharedFrameworkConfiguration(SharedFramework[] sharedFrameworks)
201203
}
202204

203205
internal IReadOnlyCollection<UsingDirectiveSyntax> GetUsings(string code) =>
204-
CSharpSyntaxTree.ParseText(code)
206+
CSharpSyntaxTree.ParseText(code, parseOptions)
205207
.GetRoot()
206208
.DescendantNodes()
207209
.OfType<UsingDirectiveSyntax>()
@@ -210,7 +212,7 @@ internal IReadOnlyCollection<UsingDirectiveSyntax> GetUsings(string code) =>
210212
internal void TrackUsings(IReadOnlyCollection<UsingDirectiveSyntax> usingsToAdd) =>
211213
usings.UnionWith(usingsToAdd);
212214

213-
private IReadOnlyCollection<MetadataReference> CreateDefaultReferences(string assemblyPath, IReadOnlyCollection<string> assemblies)
215+
private List<PortableExecutableReference> CreateDefaultReferences(string assemblyPath, IReadOnlyCollection<string> assemblies)
214216
{
215217
return assemblies
216218
.AsParallel()

0 commit comments

Comments
 (0)