diff --git a/LearnJsonEverything.LessonEditor/MainWindow.xaml.cs b/LearnJsonEverything.LessonEditor/MainWindow.xaml.cs index 7a37d68..83ea4b9 100644 --- a/LearnJsonEverything.LessonEditor/MainWindow.xaml.cs +++ b/LearnJsonEverything.LessonEditor/MainWindow.xaml.cs @@ -8,7 +8,6 @@ using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; -using LearnJsonEverything.Services; namespace LearnJsonEverything.LessonEditor; @@ -20,7 +19,5 @@ public partial class MainWindow : Window public MainWindow() { InitializeComponent(); - - CompilationHelpers.TestOnly_SetReferences(ReferenceLoader.Load()); } } diff --git a/LearnJsonEverything.LessonEditor/ReferenceLoader.cs b/LearnJsonEverything.LessonEditor/ReferenceLoader.cs deleted file mode 100644 index ed8028f..0000000 --- a/LearnJsonEverything.LessonEditor/ReferenceLoader.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Json.JsonE; -using Json.Logic; -using Json.More; -using Json.Path; -using Json.Schema; -using Json.Schema.Generation; -using Microsoft.CodeAnalysis; - -namespace LearnJsonEverything.LessonEditor; - -public static class ReferenceLoader -{ - static ReferenceLoader() - { - // force some assemblies to load - Load>(); - Load>(); - Load(); - Load(); - Load(); - Load(); - Load(); - } - - private static void Load(){} - - public static MetadataReference[] Load() - { - var refs = AppDomain.CurrentDomain.GetAssemblies(); - var assemblies = refs - .Where(x => !x.IsDynamic) - .OrderBy(x => x.FullName) - .ToArray(); - - var references = new MetadataReference[assemblies.Length]; - int i = 0; - foreach (var assembly in assemblies) - { - Console.WriteLine($"Loading {assembly.FullName}..."); - references[i] = MetadataReference.CreateFromFile(assembly.Location); - i++; - } - - return references; - } - -} \ No newline at end of file diff --git a/LearnJsonEverything.Tests/ProvidedSolutionTests.cs b/LearnJsonEverything.Tests/ProvidedSolutionTests.cs index 7a9b9aa..41d7805 100644 --- a/LearnJsonEverything.Tests/ProvidedSolutionTests.cs +++ b/LearnJsonEverything.Tests/ProvidedSolutionTests.cs @@ -12,12 +12,6 @@ public class ProvidedSolutionTests PropertyNameCaseInsensitive = true }; - [OneTimeSetUp] - public void Setup() - { - CompilationHelpers.TestOnly_SetReferences(ReferenceLoader.Load()); - } - private static IEnumerable GetLessons(string filename) { var json = File.ReadAllText(filename); diff --git a/LearnJsonEverything.Tests/ReferenceLoader.cs b/LearnJsonEverything.Tests/ReferenceLoader.cs deleted file mode 100644 index d32e8d9..0000000 --- a/LearnJsonEverything.Tests/ReferenceLoader.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Json.JsonE; -using Json.Logic; -using Json.More; -using Json.Patch; -using Json.Path; -using Json.Schema; -using Json.Schema.Generation; -using Microsoft.CodeAnalysis; - -namespace LearnJsonEverything.Tests; - -public static class ReferenceLoader -{ - static ReferenceLoader() - { - // force some assemblies to load - Load>(); - Load>(); - Load(); - Load(); - Load(); - Load(); - Load(); - Load(); - } - - private static void Load(){} - - public static MetadataReference[] Load() - { - var refs = AppDomain.CurrentDomain.GetAssemblies(); - var assemblies = refs - .Where(x => !x.IsDynamic) - .OrderBy(x => x.FullName) - .ToArray(); - - var references = new MetadataReference[assemblies.Length]; - int i = 0; - foreach (var assembly in assemblies) - { - Console.WriteLine($"Loading {assembly.FullName}..."); - references[i] = MetadataReference.CreateFromFile(assembly.Location); - i++; - } - - return references; - } - -} \ No newline at end of file diff --git a/LearnJsonEverything/LearnJsonEverything.csproj b/LearnJsonEverything/LearnJsonEverything.csproj index 3271d40..9f90498 100644 --- a/LearnJsonEverything/LearnJsonEverything.csproj +++ b/LearnJsonEverything/LearnJsonEverything.csproj @@ -4,11 +4,11 @@ net10.0 enable enable - NU1701;BL0007 - latest - false + NU1701;BL0007 + latest true false + true diff --git a/LearnJsonEverything/Program.cs b/LearnJsonEverything/Program.cs index bf08585..8bf7678 100644 --- a/LearnJsonEverything/Program.cs +++ b/LearnJsonEverything/Program.cs @@ -18,8 +18,6 @@ var host = builder.Build(); _ = host.Services.GetService(); -_ = CompilationHelpers.LoadAssemblyReferences(host.Services.GetService()!); - // Initialize theme before the UI renders so controls get the correct initial theme. var localStorage = host.Services.GetRequiredService(); var savedTheme = await localStorage.GetItemAsync("theme"); diff --git a/LearnJsonEverything/Services/CompilationHelpers.cs b/LearnJsonEverything/Services/CompilationHelpers.cs index 08ebb72..a9b9e89 100644 --- a/LearnJsonEverything/Services/CompilationHelpers.cs +++ b/LearnJsonEverything/Services/CompilationHelpers.cs @@ -1,7 +1,14 @@ -using Json.Schema.Generation.XmlComments; +using Json.Logic; +using Json.JsonE; +using Json.More; +using Json.Path; +using Json.Schema; +using Json.Schema.Generation; +using Json.Schema.Generation.XmlComments; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System.Reflection; +using System.Reflection.Metadata; using System.Runtime.Loader; using Microsoft.CodeAnalysis.Emit; using static LearnJsonEverything.Services.Iconography; @@ -11,78 +18,44 @@ namespace LearnJsonEverything.Services; public static class CompilationHelpers { private static AssemblyLoadContext? _assemblyLoadContext; - private static MetadataReference[]? _references; - private static bool _isLoading; - private static readonly bool IsBrowserRuntime = OperatingSystem.IsBrowser(); - - private static readonly string[] EnsuredAssemblies = - [ - "Json.More", - "JsonE.Net", - "JsonLogic", - "JsonPatch.Net", - "JsonPath.Net", - "JsonPointer.Net", - "JsonSchema.Net", - "JsonSchema.Net.Generation", - "LearnJsonEverything.Template", - //"Yaml2JsonNode", - ]; - - public static void TestOnly_SetReferences(MetadataReference[] references) => _references = references; - - public static async Task LoadAssemblyReferences(HttpClient client) + private static readonly Compilation _baseCompilation = CSharpCompilation.Create("BaseCompilation") + .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) + .AddReferences(GetAssemblyReferences()); + + static CompilationHelpers() { - if (_references is null) - { - if (_isLoading) return null; - _isLoading = true; + // force some assemblies to load + _ = typeof(ILessonRunner); + _ = typeof(EnumStringConverter); + _ = typeof(JsonSchema); + _ = typeof(MinimumAttribute); + _ = typeof(JsonPath); + _ = typeof(JsonFunction); + _ = typeof(Rule); + } - try - { - var refs = AppDomain.CurrentDomain.GetAssemblies(); - var names = refs - .Where(x => !x.IsDynamic) - .Select(x => x.FullName!.Split(',')[0]) - .Concat(EnsuredAssemblies) - .Distinct() - .OrderBy(x => x) - .ToArray(); - - var references = new List(names.Length); - foreach (var assemblyName in names) - { - var source = $"/_framework/{assemblyName}.dll"; - try - { - Console.WriteLine($"Loading {assemblyName}..."); - var bytes = await client.GetByteArrayAsync(source); - using var stream = new MemoryStream(bytes, writable: false); - references.Add(MetadataReference.CreateFromStream(stream)); - } - catch (Exception e) - { - Console.WriteLine(e); - Console.WriteLine(source); - } - } - - _references = [.. references]; - } - finally - { - _isLoading = false; - } + private static unsafe MetadataReference? TryCreateReferenceFromRawMetadata(Assembly assembly) + { + if (!assembly.TryGetRawMetadata(out var metadataBlob, out var metadataLength)) + { + return null; } - - return _references; + var metadata = ModuleMetadata.CreateFromMetadata((nint)metadataBlob, metadataLength, () => GC.KeepAlive(assembly)); + var location = assembly.Location; + if (location == "") + { + location = null; + } + return AssemblyMetadata.Create(metadata).GetReference(filePath: location, display: assembly.GetName().Name); } + public static IEnumerable GetAssemblyReferences() => AppDomain.CurrentDomain.GetAssemblies() + .Where(assembly => !assembly.IsDynamic) + .Select(TryCreateReferenceFromRawMetadata) + .Where(reference => reference is not null)!; + public static (ILessonRunner?, string[]) GetRunner(LessonData lesson) { - if (_references is null) - return (null, ["Compilation assemblies still loading. Please wait until complete and try again."]); - var fullSource = lesson.UserCode ?? string.Empty; Console.WriteLine($"Compiling...\n\n{fullSource}"); @@ -90,16 +63,15 @@ public static (ILessonRunner?, string[]) GetRunner(LessonData lesson) var syntaxTree = CSharpSyntaxTree.ParseText(fullSource, new CSharpParseOptions(LanguageVersion.Latest)); var assemblyPath = Path.ChangeExtension(Path.GetTempFileName(), "dll"); - var compilation = CSharpCompilation.Create(Path.GetFileName(assemblyPath)) - .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) - .AddReferences(_references) + var compilation = _baseCompilation + .WithAssemblyName(Path.GetFileName(assemblyPath)) .AddSyntaxTrees(syntaxTree); using var dllStream = new MemoryStream(); using var pdbStream = new MemoryStream(); using var xmlStream = new MemoryStream(); EmitResult emitResult; - if (IsBrowserRuntime) + if (OperatingSystem.IsBrowser()) { // Avoid debugger-agent assertions in WASM by not emitting debug symbols. emitResult = compilation.Emit(dllStream, xmlDocumentationStream: xmlStream); @@ -127,14 +99,14 @@ public static (ILessonRunner?, string[]) GetRunner(LessonData lesson) dllStream.Position = 0; xmlStream.Position = 0; - if (!IsBrowserRuntime) + if (!OperatingSystem.IsBrowser()) pdbStream.Position = 0; #pragma warning disable IL2026 #pragma warning disable IL2072 #pragma warning disable IL2070 Assembly assembly; - if (IsBrowserRuntime) + if (OperatingSystem.IsBrowser()) { assembly = Assembly.Load(dllStream.ToArray()); }