Skip to content

Commit 299ed03

Browse files
committedJun 6, 2024·
refactor and print source to be compiled
1 parent 5762fc8 commit 299ed03

File tree

4 files changed

+72
-45
lines changed

4 files changed

+72
-45
lines changed
 

Diff for: ‎LearnJsonEverything/Services/CompilationHelpers.cs

+58-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CSharp;
3+
using System.Reflection;
4+
using static LearnJsonEverything.Services.Iconography;
25

36
namespace LearnJsonEverything.Services;
47

58
public static class CompilationHelpers
69
{
710
private static MetadataReference[]? _references;
811

9-
private static readonly string[] _ensuredAssemblies =
12+
private static readonly string[] EnsuredAssemblies =
1013
[
1114
"Json.More",
1215
"JsonPointer.Net",
@@ -22,7 +25,7 @@ public static async Task<MetadataReference[]> LoadAssemblyReferences(HttpClient
2225
var names = refs
2326
.Where(x => !x.IsDynamic)
2427
.Select(x => x.FullName!.Split(',')[0])
25-
.Concat(_ensuredAssemblies)
28+
.Concat(EnsuredAssemblies)
2629
.OrderBy(x => x)
2730
.ToArray();
2831

@@ -50,4 +53,57 @@ public static async Task<MetadataReference[]> LoadAssemblyReferences(HttpClient
5053

5154
return _references;
5255
}
56+
57+
public static (ILessonRunner<T>?, string[]) GetRunner<T>(LessonData lesson, string userCode)
58+
{
59+
if (_references is null)
60+
throw new Exception("Compilation assemblies not loaded.");
61+
62+
var fullSource = lesson.ContextCode
63+
.Replace("/* USER CODE */", userCode);
64+
65+
Console.WriteLine($"Compiling...\n\n{fullSource}");
66+
67+
var syntaxTree = CSharpSyntaxTree.ParseText(fullSource);
68+
var assemblyPath = Path.ChangeExtension(Path.GetTempFileName(), "dll");
69+
70+
var compilation = CSharpCompilation.Create(Path.GetFileName(assemblyPath))
71+
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
72+
.AddReferences(_references)
73+
.AddSyntaxTrees(syntaxTree);
74+
75+
using var dllStream = new MemoryStream();
76+
using var pdbStream = new MemoryStream();
77+
using var xmlStream = new MemoryStream();
78+
var emitResult = compilation.Emit(dllStream, pdbStream, xmlStream);
79+
if (!emitResult.Success)
80+
{
81+
var diagnostics = new List<string>();
82+
foreach (var diagnostic in emitResult.Diagnostics)
83+
{
84+
var icon = diagnostic.Severity switch
85+
{
86+
DiagnosticSeverity.Info => MessageIcon,
87+
DiagnosticSeverity.Warning => WarnIcon,
88+
DiagnosticSeverity.Error => ErrorIcon,
89+
_ => string.Empty
90+
};
91+
diagnostics.Add($"{icon} {diagnostic.GetMessage()}");
92+
}
93+
return (null, [.. diagnostics]);
94+
}
95+
96+
#pragma warning disable IL2026
97+
#pragma warning disable IL2072
98+
#pragma warning disable IL2070
99+
var assembly = Assembly.Load(dllStream.ToArray());
100+
101+
var type = assembly.DefinedTypes.Single(x => !x.IsInterface && x.ImplementedInterfaces.Contains(typeof(ILessonRunner<T>)));
102+
var runner = (ILessonRunner<T>)Activator.CreateInstance(type)!;
103+
#pragma warning restore IL2070
104+
#pragma warning restore IL2072
105+
#pragma warning restore IL2026
106+
107+
return (runner, []);
108+
}
53109
}

Diff for: ‎LearnJsonEverything/Services/Iconography.cs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace LearnJsonEverything.Services;
2+
3+
public static class Iconography
4+
{
5+
public const string SuccessIcon = "✔";
6+
public const string ErrorIcon = "❌";
7+
public const string WarnIcon = "⚠";
8+
public const string MessageIcon = "ⓘ";
9+
}

Diff for: ‎LearnJsonEverything/Services/Runners/SchemaRunner.cs

+4-42
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,6 @@ public class SchemaTest
3636
3737
""";
3838

39-
private const string SuccessIcon = "✔";
40-
private const string ErrorIcon = "❌";
41-
private const string WarnIcon = "⚠";
42-
private const string MessageIcon = "ⓘ";
43-
4439
public static string BuildInstructions(LessonData lesson) => Instructions
4540
.Replace("/* TITLE */", lesson.Title)
4641
.Replace("/* INSTRUCTIONS */", lesson.Instructions)
@@ -61,52 +56,19 @@ .. tests.Select(test => $"|`{test.Instance.AsJsonString()}`|{test.IsValid}|")
6156
}
6257

6358
[RequiresUnreferencedCode("")]
64-
public static string[] Run(string userCode, LessonData lesson, MetadataReference[] references)
59+
public static string[] Run(string userCode, LessonData lesson)
6560
{
66-
var fullSource = lesson.ContextCode
67-
.Replace("/* USER CODE */", userCode);
68-
69-
var syntaxTree = CSharpSyntaxTree.ParseText(fullSource);
70-
var assemblyPath = Path.ChangeExtension(Path.GetTempFileName(), "dll");
71-
72-
var compilation = CSharpCompilation.Create(Path.GetFileName(assemblyPath))
73-
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
74-
.AddReferences(references)
75-
.AddSyntaxTrees(syntaxTree);
76-
77-
using var dllStream = new MemoryStream();
78-
using var pdbStream = new MemoryStream();
79-
using var xmlStream = new MemoryStream();
80-
var emitResult = compilation.Emit(dllStream, pdbStream, xmlStream);
81-
if (!emitResult.Success)
82-
{
83-
var diagnostics = new List<string>();
84-
foreach (var diagnostic in emitResult.Diagnostics)
85-
{
86-
var icon = diagnostic.Severity switch
87-
{
88-
DiagnosticSeverity.Info => MessageIcon,
89-
DiagnosticSeverity.Warning => WarnIcon,
90-
DiagnosticSeverity.Error => ErrorIcon,
91-
_ => string.Empty
92-
};
93-
diagnostics.Add($"{icon} {diagnostic.GetMessage()}");
94-
}
95-
return [.. diagnostics];
96-
}
97-
98-
var assembly = Assembly.Load(dllStream.ToArray());
61+
var (runner, errors) = CompilationHelpers.GetRunner<EvaluationResults>(lesson, userCode);
9962

100-
var type = assembly.DefinedTypes.Single(x => !x.IsInterface && x.ImplementedInterfaces.Contains(typeof(ILessonRunner<EvaluationResults>)));
101-
var runner = (ILessonRunner<EvaluationResults>) Activator.CreateInstance(type)!;
63+
if (runner is null) return errors;
10264

10365
var tests = lesson.Tests.Deserialize(SerializerContext.Default.SchemaTestArray)!;
10466
var results = new List<string>();
10567

10668
foreach (var test in tests)
10769
{
10870
var result = runner.Run(new JsonObject { ["instance"] = test.Instance });
109-
results.Add($"{(test.IsValid == result.IsValid ? SuccessIcon : ErrorIcon)} {test.Instance.AsJsonString()}");
71+
results.Add($"{(test.IsValid == result.IsValid ? Iconography.SuccessIcon : Iconography.ErrorIcon)} {test.Instance.AsJsonString()}");
11072
}
11173

11274
// run the code

Diff for: ‎LearnJsonEverything/Shared/Teacher.razor

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
var userCode = await _codeEditor.GetValue();
7171

7272
await _outputEditor.SetValue("");
73-
var results = SchemaRunner.Run(userCode, _currentLesson!, await CompilationHelpers.LoadAssemblyReferences(Client));
73+
var results = SchemaRunner.Run(userCode, _currentLesson!);
7474
await _outputEditor.SetValue(string.Join(Environment.NewLine, results!));
7575
}
7676
catch (Exception e)

0 commit comments

Comments
 (0)
Please sign in to comment.