Skip to content

Commit 5762fc8

Browse files
committed
explicitly list dlls to load; support trimming better
1 parent 4196ce3 commit 5762fc8

File tree

11 files changed

+130
-102
lines changed

11 files changed

+130
-102
lines changed

LearnJsonEverything/LearnJsonEverything.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
<Nullable>enable</Nullable>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<NoWarn>NU1701</NoWarn>
8-
<PublishTrimmed>false</PublishTrimmed>
98
<LangVersion>latest</LangVersion>
109
<WasmEnableWebcil>false</WasmEnableWebcil>
10+
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
1111
</PropertyGroup>
1212

1313
<ItemGroup>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using Microsoft.CodeAnalysis;
2+
3+
namespace LearnJsonEverything.Services;
4+
5+
public static class CompilationHelpers
6+
{
7+
private static MetadataReference[]? _references;
8+
9+
private static readonly string[] _ensuredAssemblies =
10+
[
11+
"Json.More",
12+
"JsonPointer.Net",
13+
"JsonSchema.Net",
14+
"LearnJsonEverything.Template",
15+
];
16+
17+
public static async Task<MetadataReference[]> LoadAssemblyReferences(HttpClient client)
18+
{
19+
if (_references is null)
20+
{
21+
var refs = AppDomain.CurrentDomain.GetAssemblies();
22+
var names = refs
23+
.Where(x => !x.IsDynamic)
24+
.Select(x => x.FullName!.Split(',')[0])
25+
.Concat(_ensuredAssemblies)
26+
.OrderBy(x => x)
27+
.ToArray();
28+
29+
var references = new MetadataReference[names.Length];
30+
int i = 0;
31+
foreach (var assemblyName in names)
32+
{
33+
var source = $"/_framework/{assemblyName}.dll";
34+
try
35+
{
36+
var stream = await client.GetStreamAsync(source);
37+
Console.WriteLine($"Loading {assemblyName}...");
38+
references[i] = MetadataReference.CreateFromStream(stream);
39+
i++;
40+
}
41+
catch (Exception e)
42+
{
43+
Console.WriteLine(e);
44+
Console.WriteLine(source);
45+
}
46+
}
47+
48+
_references = references;
49+
}
50+
51+
return _references;
52+
}
53+
}

LearnJsonEverything/Services/DataManager.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class DataManager
1010
public DataManager(ILocalStorageService localStorage)
1111
{
1212
_localStorage = localStorage;
13-
_cache = new Dictionary<string, string>();
13+
_cache = [];
1414
}
1515

1616
public async Task Set(string key, string value)
@@ -26,7 +26,10 @@ public async Task Set(string key, string value)
2626

2727
value = await _localStorage.GetItemAsync<string>(key);
2828

29-
_cache[key] = value;
29+
if (value is null)
30+
_cache.Remove(key);
31+
else
32+
_cache[key] = value;
3033

3134
return value;
3235
}

LearnJsonEverything/Services/LessonData.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System.Text.Json.Nodes;
22

3+
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
4+
35
namespace LearnJsonEverything.Services;
46

57
public class LessonData
@@ -9,5 +11,5 @@ public class LessonData
911
public string Title { get; set; }
1012
public string Instructions { get; set; }
1113
public string ContextCode { get; set; }
12-
public JsonArray? Tests { get; set; }
14+
public JsonArray Tests { get; set; }
1315
}

LearnJsonEverything/Services/LessonPlan.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Text.Json.Serialization;
1+
using System.Text.Json;
2+
using System.Text.Json.Serialization;
23

34
namespace LearnJsonEverything.Services;
45

@@ -37,4 +38,21 @@ public LessonPlan(LessonData[] lessonData)
3738

3839
return _lessonData[index];
3940
}
41+
}
42+
43+
public class LessonPlanJsonConverter : JsonConverter<LessonPlan>
44+
{
45+
public override LessonPlan? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
46+
{
47+
#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
48+
var lessonData = JsonSerializer.Deserialize<LessonData[]>(ref reader, options)!;
49+
#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
50+
51+
return new LessonPlan(lessonData);
52+
}
53+
54+
public override void Write(Utf8JsonWriter writer, LessonPlan value, JsonSerializerOptions options)
55+
{
56+
throw new NotImplementedException();
57+
}
4058
}

LearnJsonEverything/Services/LessonPlanJsonConverter.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.

LearnJsonEverything/Services/Runners/SchemaRunner.cs

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Reflection;
1+
using System.Diagnostics.CodeAnalysis;
2+
using System.Reflection;
23
using System.Text.Json;
34
using System.Text.Json.Nodes;
45
using Json.More;
@@ -12,7 +13,7 @@ namespace LearnJsonEverything.Services.Runners;
1213

1314
public static class SchemaRunner
1415
{
15-
private class SchemaTest
16+
public class SchemaTest
1617
{
1718
public JsonNode? Instance { get; set; }
1819
public bool IsValid { get; set; }
@@ -40,10 +41,6 @@ private class SchemaTest
4041
private const string WarnIcon = "⚠";
4142
private const string MessageIcon = "ⓘ";
4243

43-
//public static string ErrorIcon = "<span style=\"foreground:red;\">❌</span>";
44-
//public static string WarnIcon = "<span style=\"foreground:amber;\">⚠</span>";
45-
//public static string MessageIcon = "<span style=\"foreground:lightblue;\">ⓘ</span>";
46-
4744
public static string BuildInstructions(LessonData lesson) => Instructions
4845
.Replace("/* TITLE */", lesson.Title)
4946
.Replace("/* INSTRUCTIONS */", lesson.Instructions)
@@ -52,21 +49,18 @@ public static string BuildInstructions(LessonData lesson) => Instructions
5249

5350
private static string BuildTestList(JsonArray testData)
5451
{
55-
var tests = testData.Deserialize<SchemaTest[]>(SerializerOptions);
56-
var lines = new List<string>
57-
{
52+
var tests = testData.Deserialize(SerializerContext.Default.SchemaTestArray)!;
53+
string[] lines =
54+
[
5855
"| Instance | Is Valid |",
59-
"|:-|:-:|"
60-
};
61-
62-
foreach (var test in tests)
63-
{
64-
lines.Add($"|`{test.Instance.AsJsonString()}`|{test.IsValid}|");
65-
}
56+
"|:-|:-:|",
57+
.. tests.Select(test => $"|`{test.Instance.AsJsonString()}`|{test.IsValid}|")
58+
];
6659

6760
return string.Join(Environment.NewLine, lines);
6861
}
6962

63+
[RequiresUnreferencedCode("")]
7064
public static string[] Run(string userCode, LessonData lesson, MetadataReference[] references)
7165
{
7266
var fullSource = lesson.ContextCode
@@ -106,7 +100,7 @@ public static string[] Run(string userCode, LessonData lesson, MetadataReference
106100
var type = assembly.DefinedTypes.Single(x => !x.IsInterface && x.ImplementedInterfaces.Contains(typeof(ILessonRunner<EvaluationResults>)));
107101
var runner = (ILessonRunner<EvaluationResults>) Activator.CreateInstance(type)!;
108102

109-
var tests = lesson.Tests.Deserialize<SchemaTest[]>(SerializerOptions);
103+
var tests = lesson.Tests.Deserialize(SerializerContext.Default.SchemaTestArray)!;
110104
var results = new List<string>();
111105

112106
foreach (var test in tests)
@@ -117,12 +111,6 @@ public static string[] Run(string userCode, LessonData lesson, MetadataReference
117111

118112
// run the code
119113

120-
return results.ToArray();
121-
}
122-
123-
private static string ToLiteral(this string valueTextForCompiler)
124-
{
125-
var formatted = SymbolDisplay.FormatLiteral(valueTextForCompiler, true);
126-
return formatted;
114+
return [.. results];
127115
}
128116
}
Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
1-
using System.Text.Encodings.Web;
2-
using System.Text.Json;
1+
using LearnJsonEverything.Services.Runners;
2+
using Microsoft.CodeAnalysis.CSharp;
3+
using System.Text.Json.Nodes;
4+
using System.Text.Json.Serialization;
5+
using Json.Schema;
36

4-
namespace LearnJsonEverything.Services
7+
namespace LearnJsonEverything.Services;
8+
9+
public static class SerializationHelpers
510
{
6-
public static class SerializationHelpers
11+
public static string ToLiteral(this string valueTextForCompiler)
712
{
8-
public static readonly JsonSerializerOptions SerializerOptions = new()
9-
{
10-
WriteIndented = true,
11-
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
12-
PropertyNameCaseInsensitive = true
13-
};
13+
var formatted = SymbolDisplay.FormatLiteral(valueTextForCompiler, true);
14+
return formatted;
1415
}
1516
}
17+
18+
[JsonSerializable(typeof(JsonSchema))]
19+
[JsonSerializable(typeof(JsonNode))]
20+
[JsonSerializable(typeof(JsonObject))]
21+
[JsonSerializable(typeof(JsonArray))]
22+
[JsonSerializable(typeof(LessonPlan))]
23+
[JsonSerializable(typeof(LessonData))]
24+
[JsonSerializable(typeof(LessonData[]))]
25+
[JsonSerializable(typeof(SchemaRunner.SchemaTest))]
26+
[JsonSerializable(typeof(SchemaRunner.SchemaTest[]))]
27+
[JsonSourceGenerationOptions(WriteIndented = true, PropertyNameCaseInsensitive = true)]
28+
internal partial class SerializerContext : JsonSerializerContext;

LearnJsonEverything/Shared/Feature.razor

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424

2525
@code {
26+
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
2627
[Parameter]
2728
public string Title { get; set; }
2829
[Parameter]
@@ -35,4 +36,5 @@
3536
public string DocsUrl { get; set; }
3637
[Parameter]
3738
public bool Flipped { get; set; }
39+
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
3840
}

LearnJsonEverything/Shared/Teacher.razor

Lines changed: 11 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@
5151
#pragma warning disable CS8618
5252
private StandaloneCodeEditor _codeEditor;
5353
private StandaloneCodeEditor _outputEditor;
54-
private MetadataReference[] _references;
5554
private LessonPlan _lessons;
5655
private LessonData? _currentLesson;
5756

@@ -71,7 +70,7 @@
7170
var userCode = await _codeEditor.GetValue();
7271

7372
await _outputEditor.SetValue("");
74-
var results = SchemaRunner.Run(userCode, _currentLesson!, _references);
73+
var results = SchemaRunner.Run(userCode, _currentLesson!, await CompilationHelpers.LoadAssemblyReferences(Client));
7574
await _outputEditor.SetValue(string.Join(Environment.NewLine, results!));
7675
}
7776
catch (Exception e)
@@ -81,76 +80,45 @@
8180
}
8281
}
8382

84-
private Task PreviousLesson()
83+
private void PreviousLesson()
8584
{
8685
_currentLesson = _lessons.GetPrevious(_currentLesson?.Id);
87-
return LoadLesson();
86+
LoadLesson();
8887
}
8988

90-
private Task NextLesson()
89+
private void NextLesson()
9190
{
9291
_currentLesson = _lessons.GetNext(_currentLesson?.Id);
93-
return LoadLesson();
92+
LoadLesson();
9493
}
9594

96-
private Task Reset()
95+
private void Reset()
9796
{
98-
return LoadLesson();
97+
LoadLesson();
9998
}
10099

101-
private async Task LoadLesson()
100+
private void LoadLesson()
102101
{
103102
_currentLesson ??= _lessons[0];
104103
Instructions = SchemaRunner.BuildInstructions(_currentLesson);
105104
}
106105

107106
protected override async Task OnInitializedAsync()
108107
{
109-
await LoadAssemblyReferences();
108+
_ = await CompilationHelpers.LoadAssemblyReferences(Client);
110109
await DownloadLessonPlan();
111110

112-
await LoadLesson();
111+
LoadLesson();
113112

114113
await base.OnInitializedAsync();
115114
}
116115

117-
private async Task LoadAssemblyReferences()
118-
{
119-
var refs = AppDomain.CurrentDomain.GetAssemblies();
120-
121-
var references = new MetadataReference[refs.Length];
122-
123-
foreach (var reference in refs.Where(x => !x.IsDynamic))
124-
{
125-
Console.WriteLine($"{reference.FullName}\n Location - {reference.Location}\n");
126-
}
127-
128-
int i = 0;
129-
foreach (var reference in refs.Where(x => !x.IsDynamic))
130-
{
131-
var source = $"/_framework/{reference.FullName!.Split(',')[0]}.dll";
132-
try
133-
{
134-
var stream = await Client.GetStreamAsync(source);
135-
references[i] = MetadataReference.CreateFromStream(stream);
136-
i++;
137-
}
138-
catch (Exception e)
139-
{
140-
Console.WriteLine(e);
141-
Console.WriteLine(source);
142-
}
143-
}
144-
145-
_references = references.ToArray();
146-
}
147-
148116
private async Task DownloadLessonPlan()
149117
{
150118
var yamlText = await Client.GetStringAsync(LessonSource);
151119
var yaml = YamlSerializer.Parse(yamlText);
152120
var json = yaml.ToJsonNode().FirstOrDefault();
153121
Console.WriteLine(json.AsJsonString());
154-
_lessons = json.Deserialize<LessonPlan>(SerializerOptions)!;
122+
_lessons = json.Deserialize(SerializerContext.Default.LessonPlan)!;
155123
}
156124
}

0 commit comments

Comments
 (0)